std :: ofstream - строка буферизации не длиннее 1023 (мгновенный сброс) - PullRequest
2 голосов
/ 02 июля 2019

Когда я изменил размер буфера ofstream с помощью pubsetbuf(...), все работает нормально, за исключением случаев, когда я ставлю ofstream одну строку длиннее 1023 (в коде ниже). Это правильное поведение или я сделал что-то не так?

int main(){
    std::vector<char> rawBuf;
    std::ofstream stream;

    rawBuf.resize(20000);
    stream.rdbuf()->pubsetbuf(&rawBuf[0], 20000);

    stream.open("file.txt", std::ios_base::app);

    std::string data(1499, 'b');

    for(int i = 0; i < 10; i++)
    {   
        stream << data.substr(0, 1024) << "\n"; //1023-length string works great
        sleep(1);
    }
    stream.flush();
    stream.close();

    return 0;
}

когда есть строка длиной 1024 strace ./program показывает что-то вроде этого:

writev(3, [{iov_base=NULL, iov_len=0}, {iov_base="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., iov_len=1024}], 2) = 1024
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffcf3889ac0) = 0
writev(3, [{iov_base="\n", iov_len=1}, {iov_base="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., iov_len=1024}], 2) = 1025
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffcf3889ac0) = 0
... and so on 10x

когда есть строка длиной 1023, все выглядит нормально:

nanosleep({tv_sec=1, tv_nsec=0}, 0x7fff8e13a980) = 0
nanosleep({tv_sec=1, tv_nsec=0}, 0x7fff8e13a980) = 0
... 10x

и затем:

write(3, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., 10240) = 10240

Почему здесь одиночная запись, а раньше нет?

редактирование:

gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)

1 Ответ

0 голосов
/ 03 июля 2019

За [filebuf.virtuals] / 12 :

basic_streambuf* setbuf(char_type* s, streamsize n) override;

Эффекты: Если setbuf(0, 0) вызывается в потоке до того, как в этом потоке произошел какой-либо ввод-вывод, поток становится небуферизованным. В противном случае результаты определяются реализацией. «Небуферизованный» означает, что pbase() и pptr() всегда возвращает ноль, и вывод в файл должен появиться как можно скорее.

«Определено реализацией» включает «отлично работает», «есть только одна запись» и другие вещи. Фактически, вот что libstdc ++ 7.3.0 говорит :

Во-первых, вы уверены, что понимаете буферизацию? Особенно тот факт, что C ++, возможно, не имеет к этому никакого отношения?

Правила буферизации могут быть немного странными, но они не отличается от C. (Может быть, поэтому они могут быть немного странными.) Многие думают, что запись новой строки в выходной поток автоматически очищает выходной буфер. Это верно только тогда, когда Выходной поток, по сути, является терминалом, а не файлом или каким-либо другим устройство - и это может даже не быть правдой, так как C ++ ничего не говорит о файлы, ни терминалы. Все это зависит от системы. (The «newline-buffer-flushing only произойдет на терминалах» - это в основном правда, в системах Unix.)

Некоторые люди также считают, что отправка endl вниз только по выходному потоку пишет новую строку. Это неверно; после написания новой строки буфер также очищен. Возможно, это тот эффект, который вы хотите, когда запись на экран - выведите текст как можно скорее и т. д. - но буферизация в значительной степени теряется при выполнении этого для файла:

output << "a line of text" << endl;
output << some_data_variable << endl;
output << "another line of text" << endl; 

В этом случае нужно правильно записать данные и позволить библиотеки и система беспокоятся о буферизации. Если вам нужно новая строка, просто напишите новую строку:

output << "a line of text\n"
 << some_data_variable << '\n'
 << "another line of text\n"; 

Я также объединил выходные операторы в один оператор. Вы может сделать код красивее, переместив одну строку в начало цитируемого текста в последней строке, например.

Если вам нужно очистить буфер выше, вы можете отправить endl, если вам также нужен перевод строки или просто очистить буфер самостоятельно:

output << ...... << flush;    // can use std::flush manipulator
output.flush();               // or call a member fn 

С другой стороны, бывают случаи, когда запись в файл должна быть как запись в стандартную ошибку; буферизация не должна выполняться, потому что данные должны появляться быстро (ярким примером является файл журнала для информация, связанная с безопасностью). Способ сделать это просто выключить буферизация перед выполнением любых операций ввода-вывода (примечание это открытие считается операцией ввода / вывода):

std::ofstream    os;
std::ifstream    is;
int   i;

os.rdbuf()->pubsetbuf(0,0);
is.rdbuf()->pubsetbuf(0,0);

os.open("/foo/bar/baz");
is.open("/qux/quux/quuux");
...
os << "this data is written immediately\n";
is >> i;   // and this will probably cause a disk read 

Поскольку все аспекты буферизации обрабатываются streambuf член, необходимо получить этого члена с rdbuf(). Тогда Публичная версия setbuf может быть названа. Аргументы одинаковы как те для стандартной библиотеки ввода / вывода C (область буфера затем его размер).

Многое из этого зависит от реализации. Например, streambuf не указывает никаких действий для своего собственного setbuf() -ish функции; классы, полученные из streambuf, каждый определяет поведение это «имеет смысл» для этого класса: аргумент (0,0) отключается буферизация для filebuf, но ничего не делает для своих братьев и сестер stringbuf и strstreambuf и указание чего-либо кроме (0,0) имеет различные эффекты. Пользовательские классы, полученные из streambuf может делать все, что захочет. (Для filebuf и аргументов для (p,s) кроме нулей libstdc ++ делает то, что вы ожидаете: первые s байтов p используются в качестве буфера, который вы должны выделить и освободить.)

Последнее напоминание: обычно задействовано больше буферов, чем просто те на уровне языка / библиотеки. Буферы ядра, дисковые буферы и подобное также будет иметь эффект. Проверка и изменение тех зависит от системы.

...