Почему printf не сбрасывается после вызова, если в строке формата нет новой строки?
539
Почему printfпосле вызова не происходит сброс, если в строке формата нет новой строки? Это поведение POSIX? Как я мог printfсразу же промывать каждый раз?
Вы исследовали, происходит ли это с любым файлом или только с терминалами? это звучит как умная функция терминала, которая не выводит незавершенную строку из фоновой программы, хотя я ожидаю, что она не будет применяться к программе переднего плана.
PypeBros
7
В Cygwin bash я вижу такое же неправильное поведение, даже если в строке формата находится символ новой строки. Эта проблема является новой для Windows 7; тот же исходный код работал нормально на Windows XP. MS cmd.exe мигает, как и ожидалось. Исправление setvbuf(stdout, (char*)NULL, _IONBF, 0)работает вокруг проблемы, но, конечно, не должно быть необходимо. Я использую MSVC ++ 2008 Express. ~~~
Стив Питчерс
9
Чтобы уточнить заголовок вопроса: сам по себе printf(..)сброс не выполняется , его буферизация stdoutможет сбрасываться при просмотре новой строки (если она буферизована строкой). Он будет реагировать так же putchar('\n');, поэтому printf(..)не является особенным в этом отношении. Это противоречит тому cout << endl;, что в документации явно упоминается промывка. В документации printf вообще не упоминается промывка.
Евгений Сергеев
1
запись (/ сброс) - потенциально дорогостоящая операция, которая, вероятно, буферизована по соображениям производительности.
hanshenrik
@EvgeniSergeev: Есть ли согласие, что вопрос неправильно диагностировал проблему, и что сброс происходит, когда в выходных данных есть новая строка ? (Поместить единицу в строку формата - это один, но не единственный способ получить единицу в выводе).
Бен Фойг
Ответы:
703
stdoutПоток линии буферном по умолчанию, так что будет отображать только то , что в буфере после того, как он достигнет новой строки (или , когда это сказано). У вас есть несколько вариантов печати сразу:
Или полностью отключить буферизацию:setbuf(stdout, NULL);
Энди Росс
80
Кроме того, я просто хотел упомянуть, что, очевидно, в UNIX символ новой строки обычно очищает буфер, только если stdout является терминалом. Если вывод перенаправляется в файл, символ новой строки не сбрасывается.
Ор
5
Я чувствую, что должен добавить: я только что проверил эту теорию, и я обнаружил, что использование setlinebuf()потока, который не направлен на терминал , сбрасывается в конце каждой строки.
Додди
8
«При первоначальном открытии стандартный поток ошибок не полностью буферизован; стандартные входные и стандартные выходные потоки полностью буферизуются в том и только в том случае, если можно определить, что поток не ссылается на интерактивное устройство» - см. Этот вопрос: stackoverflow.com / questions / 5229096 /…
Сеппо Энарви
3
@RuddZwolinski Если это будет хороший канонический ответ на вопрос «почему не печатается», важно упомянуть различие между терминалом и файлом согласно «Всегда ли printf очищает буфер при встрече с новой строкой?» прямо в этом высоко голосуемом ответе, против людей, которым нужно читать комментарии ...
HostileFork говорит, что не доверяйте SE
128
Нет, это не поведение POSIX, это поведение ISO (ну, это поведение POSIX , но лишь постольку , поскольку они соответствуют ISO).
Стандартный вывод буферизуется строкой, если он может быть обнаружен для обращения к интерактивному устройству, в противном случае он полностью буферизован. Таким образом, существуют ситуации, когда printfне происходит сброс, даже если он отправляет новую строку, например:
myprog >myfile.txt
Это имеет смысл для эффективности, поскольку, если вы взаимодействуете с пользователем, они, вероятно, хотят видеть каждую строку. Если вы отправляете вывод в файл, то, скорее всего, на другом конце нет пользователя (хотя и не исключено, что он может следить за файлом). Теперь вы можете утверждать, что пользователь хочет видеть каждого персонажа, но с этим есть две проблемы.
Во-первых, это не очень эффективно. Во-вторых, первоначальный мандат ANSI C состоял в том, чтобы в первую очередь кодифицировать существующее поведение, а не придумывать новое поведение, и эти проектные решения были приняты задолго до того, как ANSI начала процесс. Даже ISO в настоящее время очень осторожно действует при изменении существующих правил в стандартах.
Что касается того, как с этим справиться, если fflush (stdout)после каждого выходного вызова, который вы хотите увидеть немедленно, это решит проблему.
В качестве альтернативы, вы можете использовать setvbufперед операцией stdout, чтобы установить его как небуферизованный, и вам не придется беспокоиться о добавлении всех этих fflushстрок в ваш код:
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
Просто имейте в виду , что может повлиять на производительность совсем немного , если будут посылать вывод в файл. Также имейте в виду, что поддержка этого определяется реализацией, а не гарантируется стандартом.
Раздел ISO C99 7.19.3/3является соответствующим битом:
Когда поток небуферизован , символы должны появиться из источника или в месте назначения как можно скорее. В противном случае символы могут накапливаться и передаваться в среду хоста или из нее в виде блока.
Когда поток полностью буферизован , символы предназначены для передачи в или из среды хоста как блок, когда буфер заполнен.
Когда поток буферизуется строкой , символы предназначены для передачи в или из среды хоста в виде блока, когда встречается символ новой строки.
Кроме того, символы предназначены для передачи в качестве блока в среду хоста, когда заполнен буфер, когда запрашивается ввод в небуферизованном потоке или когда запрашивается ввод в потоке с буферизацией строки, который требует передачи символов из среды хоста. ,
Поддержка этих характеристик зависит от реализации, и может быть затронута через setbufи setvbufфункции.
Я только что натолкнулся на сценарий, в котором даже есть '\ n', printf () не сбрасывается. Это было преодолено добавлением fflush (stdout), как вы упомянули здесь. Но мне интересно, почему '\ n' не удалось очистить буфер в printf ().
Цян Сюй
11
@QiangXu, стандартный вывод - это линейная буферизация только в том случае, если можно окончательно определить ссылку на интерактивное устройство. Так, например, если вы перенаправляете вывод с помощью myprog >/tmp/tmpfile, это полностью буферизуется, а не буферизируется строкой. По памяти определение того, является ли ваш стандартный вывод интерактивным, оставлено на усмотрение реализации.
Вероятно, так из-за эффективности и потому, что если у вас есть несколько программ, пишущих в один TTY, вы не получите чередование символов в строке. Так что, если программы A и B выводят, вы обычно получите:
program A output
program B output
program B output
program A output
program B output
Это воняет, но это лучше, чем
proprogrgraam m AB ououtputputt
prproogrgram amB A ououtputtput
program B output
Обратите внимание, что даже не гарантируется сброс на новую строку, поэтому вы должны выполнять сброс явно, если сброс имеет значение для вас.
Имейте в виду, fflush(NULL);как правило, очень плохая идея. Это снизит производительность, если у вас будет открыто много файлов, особенно в многопоточной среде, где вы будете бороться со всеми за блокировки.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
14
Примечание: библиотеки времени выполнения Microsoft не поддерживают буферизацию строки, поэтому printf("will print immediately to terminal"):
Хуже , чем printfидти сразу к терминалу в «нормальном» случае является тем фактом , что printfи fprintfполучить более крупно буферные даже в тех случаях , когда их выход для непосредственного применения. Если MS не исправит ошибки, одна программа не сможет захватить stderr и stdout из другой и определить, в какой последовательности они были отправлены.
суперкат
нет, он не распечатывает это немедленно на терминал, если не была установлена буферизация. По умолчанию используется полная буферизация
phuclv
12
stdout буферизуется, поэтому выводится только после печати новой строки.
msgstr "выводится только после печати новой строки." Не только это, но как минимум 4 других случая. буфер полный, пишите stderr(этот ответ упоминает позже), fflush(stdout), fflush(NULL).
chux - Восстановить Монику
11
по умолчанию stdout буферизуется строкой, stderr не буферизируется, а файл полностью буферизуется.
Вместо этого вы можете выполнить fprintf to stderr, который не имеет буфера. Или вы можете сбросить стандартный вывод, когда захотите. Или вы можете установить стандартный вывод для небуферизованного.
2. Буферизация в библиотеке ввода / вывода (уменьшает количество системных вызовов)
Давайте возьмем пример fprintf and write().
Когда вы звоните fprintf(), он не записывается напрямую в файл. Сначала он идет в буфер stdio в памяти программы. Оттуда это записывается в буферный кеш ядра с помощью системного вызова write. Таким образом, один из способов пропустить буфер ввода-вывода - напрямую использовать write (). Другие способы использования setbuff(stream,NULL). Это устанавливает режим буферизации на отсутствие буферизации, и данные напрямую записываются в буфер ядра. Чтобы принудительно переместить данные в буфер ядра, мы можем использовать «\ n», который в случае режима буферизации по умолчанию «линейной буферизации» очистит буфер ввода-вывода. Или мы можем использовать fflush(FILE *stream).
Теперь мы находимся в буфере ядра. Ядро (/ OS) хочет минимизировать время доступа к диску и, следовательно, читает / записывает только блоки диска. Поэтому, когда read()выдается a , который является системным вызовом и может быть вызван напрямую или через него fscanf(), ядро считывает блок диска с диска и сохраняет его в буфере. После этого данные копируются отсюда в пространство пользователя.
Точно так же fprintf()данные, полученные из буфера ввода / вывода, записываются на диск ядром. Это делает read () write () быстрее.
Теперь, чтобы заставить ядро инициировать a write(), после чего передача данных контролируется аппаратными контроллерами, есть также несколько способов. Мы можем использовать O_SYNCили похожие флаги во время записи звонков. Или мы могли бы использовать другие функции, например, fsync(),fdatasync(),sync()чтобы ядро инициировало запись, как только данные будут доступны в буфере ядра.
setvbuf(stdout, (char*)NULL, _IONBF, 0)
работает вокруг проблемы, но, конечно, не должно быть необходимо. Я использую MSVC ++ 2008 Express. ~~~printf(..)
сброс не выполняется , его буферизацияstdout
может сбрасываться при просмотре новой строки (если она буферизована строкой). Он будет реагировать так жеputchar('\n');
, поэтомуprintf(..)
не является особенным в этом отношении. Это противоречит томуcout << endl;
, что в документации явно упоминается промывка. В документации printf вообще не упоминается промывка.Ответы:
stdout
Поток линии буферном по умолчанию, так что будет отображать только то , что в буфере после того, как он достигнет новой строки (или , когда это сказано). У вас есть несколько вариантов печати сразу:Печать на
stderr
вместо использованияfprintf
(stderr
это небуферизовано по умолчанию ):Сбрасывайте стандартный вывод всякий раз, когда вам это нужно, используя
fflush
:Изменить : Из комментария Энди Росса ниже, вы также можете отключить буферизацию на стандартный вывод, используя
setbuf
:или его безопасная версия,
setvbuf
как описано здесьисточник
setbuf(stdout, NULL);
setlinebuf()
потока, который не направлен на терминал , сбрасывается в конце каждой строки.Нет, это не поведение POSIX, это поведение ISO (ну, это поведение POSIX , но лишь постольку , поскольку они соответствуют ISO).
Стандартный вывод буферизуется строкой, если он может быть обнаружен для обращения к интерактивному устройству, в противном случае он полностью буферизован. Таким образом, существуют ситуации, когда
printf
не происходит сброс, даже если он отправляет новую строку, например:Это имеет смысл для эффективности, поскольку, если вы взаимодействуете с пользователем, они, вероятно, хотят видеть каждую строку. Если вы отправляете вывод в файл, то, скорее всего, на другом конце нет пользователя (хотя и не исключено, что он может следить за файлом). Теперь вы можете утверждать, что пользователь хочет видеть каждого персонажа, но с этим есть две проблемы.
Во-первых, это не очень эффективно. Во-вторых, первоначальный мандат ANSI C состоял в том, чтобы в первую очередь кодифицировать существующее поведение, а не придумывать новое поведение, и эти проектные решения были приняты задолго до того, как ANSI начала процесс. Даже ISO в настоящее время очень осторожно действует при изменении существующих правил в стандартах.
Что касается того, как с этим справиться, если
fflush (stdout)
после каждого выходного вызова, который вы хотите увидеть немедленно, это решит проблему.В качестве альтернативы, вы можете использовать
setvbuf
перед операциейstdout
, чтобы установить его как небуферизованный, и вам не придется беспокоиться о добавлении всех этихfflush
строк в ваш код:Просто имейте в виду , что может повлиять на производительность совсем немного , если будут посылать вывод в файл. Также имейте в виду, что поддержка этого определяется реализацией, а не гарантируется стандартом.
Раздел ISO C99
7.19.3/3
является соответствующим битом:источник
myprog >/tmp/tmpfile
, это полностью буферизуется, а не буферизируется строкой. По памяти определение того, является ли ваш стандартный вывод интерактивным, оставлено на усмотрение реализации.Вероятно, так из-за эффективности и потому, что если у вас есть несколько программ, пишущих в один TTY, вы не получите чередование символов в строке. Так что, если программы A и B выводят, вы обычно получите:
Это воняет, но это лучше, чем
Обратите внимание, что даже не гарантируется сброс на новую строку, поэтому вы должны выполнять сброс явно, если сброс имеет значение для вас.
источник
Чтобы немедленно сбросить вызов
fflush(stdout)
илиfflush(NULL)
(NULL
означает сбросить все).источник
fflush(NULL);
как правило, очень плохая идея. Это снизит производительность, если у вас будет открыто много файлов, особенно в многопоточной среде, где вы будете бороться со всеми за блокировки.Примечание: библиотеки времени выполнения Microsoft не поддерживают буферизацию строки, поэтому
printf("will print immediately to terminal")
:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf
источник
printf
идти сразу к терминалу в «нормальном» случае является тем фактом , чтоprintf
иfprintf
получить более крупно буферные даже в тех случаях , когда их выход для непосредственного применения. Если MS не исправит ошибки, одна программа не сможет захватить stderr и stdout из другой и определить, в какой последовательности они были отправлены.stdout буферизуется, поэтому выводится только после печати новой строки.
Чтобы получить немедленный вывод, либо:
источник
fflush(stdout)
.stderr
(этот ответ упоминает позже),fflush(stdout)
,fflush(NULL)
.по умолчанию stdout буферизуется строкой, stderr не буферизируется, а файл полностью буферизуется.
источник
Вместо этого вы можете выполнить fprintf to stderr, который не имеет буфера. Или вы можете сбросить стандартный вывод, когда захотите. Или вы можете установить стандартный вывод для небуферизованного.
источник
Используйте
setbuf(stdout, NULL);
для отключения буферизации.источник
Обычно существует 2 уровня
1. Кеш ядра буфера (ускоряет чтение / запись)
2. Буферизация в библиотеке ввода / вывода (уменьшает количество системных вызовов)
Давайте возьмем пример
fprintf and write()
.Когда вы звоните
fprintf()
, он не записывается напрямую в файл. Сначала он идет в буфер stdio в памяти программы. Оттуда это записывается в буферный кеш ядра с помощью системного вызова write. Таким образом, один из способов пропустить буфер ввода-вывода - напрямую использовать write (). Другие способы использованияsetbuff(stream,NULL)
. Это устанавливает режим буферизации на отсутствие буферизации, и данные напрямую записываются в буфер ядра. Чтобы принудительно переместить данные в буфер ядра, мы можем использовать «\ n», который в случае режима буферизации по умолчанию «линейной буферизации» очистит буфер ввода-вывода. Или мы можем использоватьfflush(FILE *stream)
.Теперь мы находимся в буфере ядра. Ядро (/ OS) хочет минимизировать время доступа к диску и, следовательно, читает / записывает только блоки диска. Поэтому, когда
read()
выдается a , который является системным вызовом и может быть вызван напрямую или через негоfscanf()
, ядро считывает блок диска с диска и сохраняет его в буфере. После этого данные копируются отсюда в пространство пользователя.Точно так же
fprintf()
данные, полученные из буфера ввода / вывода, записываются на диск ядром. Это делает read () write () быстрее.Теперь, чтобы заставить ядро инициировать a
write()
, после чего передача данных контролируется аппаратными контроллерами, есть также несколько способов. Мы можем использоватьO_SYNC
или похожие флаги во время записи звонков. Или мы могли бы использовать другие функции, например,fsync(),fdatasync(),sync()
чтобы ядро инициировало запись, как только данные будут доступны в буфере ядра.источник