fputs() с новой строкой, как puts() в C

В C puts(string); выведет string на стандартный вывод, за которым следует новая строка. fputs(fileptr, string);, с другой стороны, запишет string в fileptr без завершающей новой строки. Есть ли какая-нибудь функция, подобная fputs(), которая добавляет новую строку, или мне следует придерживаться fprintf(fileptr, "%s\n", string);, как я использовал?

fputs() кажется мне более эффективным, чем fprintf(), поскольку он не анализирует ввод. Я знаю, что мог бы также использовать

fputs(string, fileptr);
fputc('\n', fileptr);

но мне было интересно, есть ли способ сделать это с помощью записи на один диск.

Я попытался выяснить, как puts() добавляет новую строку (поскольку printf() на самом деле является просто оболочкой для vfprintf(stdout, ...), я подумал, что то же самое может быть верно для puts() и fputs()), но, как ни странно, я не могу найти puts() в исходном коде glibc.


person Billy    schedule 11.07.2018    source источник
comment
кажется более эффективным, чем ... какое профилирование вы сделали, что показывает существенную разницу в двух неприемлемых и почему? Тем более, что дисковый ввод-вывод, даже с буферизацией, на порядки больше, чем циклы процессора?   -  person    schedule 11.07.2018
comment
Оба этих вызова функций (fputs(fileptr, string); и fputc(fileptr, '\n');) неверны. Параметр файлового потока идет последним, за исключением fprintf() и fscanf() и их вариантов. Поэтому вызовы функций должны быть fputs(string, fileptr); и fputc('\n', fileptr);.   -  person Jonathan Leffler    schedule 11.07.2018


Ответы (2)


Мне было интересно, есть ли способ сделать это с помощью записи на один диск.

Вы чрезмерно оптимизируете вещи. Запись на диск буферизируется, если вы случайно не напортачили с настройками буферизации или используете очень плохую реализацию libc. В общем, лучший способ добиться желаемого — это

fputs(string, fileptr);
fputc('\n', fileptr);

руки вниз. Если вас не интересует оптимизация, или вы считаете, что ваш компилятор ее оптимизирует, вы можете использовать

fprintf(fileptr, "%s\n", string);

ему нужно будет проанализировать строку формата - и даже тогда внутри использовать эквивалент fputs для записи %s и fputc для печати новой строки.


Однако есть одна проблема — имейте в виду, что структура FILE обычно имеет какую-то блокировку для безопасности потоков. fputs + fputc потребовалось бы получить блокировку дважды, тогда как fprintf, вероятно, сделал бы это только один раз. Однако в однопоточном приложении блокировка не оспаривалась. Но может быть просто для сложного формата и многопоточной программы парсинг fprintf быстрее, чем получение блокировок для отдельных операций. Другое дело, что вызов из другого потока может чередовать fputs/fputc, но fprintf предполагается атомарным.


P.S. код puts Glibc находится в libio/ioputs.c.

person Antti Haapala    schedule 11.07.2018
comment
Это лучше, чем fprintf(fileptr, %s\n, string);? - person Billy; 11.07.2018
comment
Ты здесь далеко в сорняках, @Billy. Хороший компилятор, скорее всего, оптимизирует оба, чтобы они были идентичными. Это определяется реализацией, но должно быть близким среди компиляторов. Суть буферизации заключается в том, что операции ввода-вывода читаются и записываются из буфера BUFSIZ (см. исходный код библиотеки C IO_BUFSIZ (обычно 8192 в Linux и 512 в windows). Так что с точки зрения ввода-вывода это будет основная промывка. Идеологическое отличие состоит в том, что fprintf является вариативной функцией, которая обычно имеет дополнительные накладные расходы по сравнению с функцией с фиксированными параметрами, но оптимизация справляется с большинством. - person David C. Rankin; 11.07.2018
comment
Вот ссылка, которую я искал, теперь она находится в _G_BUFSIZ в Gnu libc (_G_config.h) (где _IO_BUFSIZ определяется как _G_BUFSIZ, а BUFSIZ определяется как _IO_BUFSIZ в отдельных файлах заголовков) - person David C. Rankin; 11.07.2018
comment
@DavidC.Rankin Спасибо за статью. Вы, кажется, пишете, я запускал strings в своей программе после компиляции с fprintf(fileptr, "%s\n", string);, и я не видел никаких вхождений %s. - person Billy; 11.07.2018
comment
Проще просто скинуть на сборку и посмотреть на это так. gcc -S -O2 -masm=intel -o yourfile.asm yourfile.c для сброса yourfile.c в yourfile.asm (используя синтаксис Intel, который мне кажется проще, чем ATT - на ваше усмотрение). Скорее всего, вы обнаружите, что ваш фактический вывод сводится к системному вызову write. (вы можете поиграться с уровнями оптимизации -O0, -O1, -O2, -O3, -Ofast и посмотреть, как оптимизации влияют на генерацию кода. (и быть глубоко в траве не так уж и плохо, но обычно вы либо профилируете, либо выгружаете сборку, или в исходном коде C-Standard или библиотеки, чтобы получить окончательный ответ) - person David C. Rankin; 11.07.2018
comment
Если вам нужно сгруппировать операции над одним файловым потоком, приложение POSIX может использовать flockfile() и funlockfile() и ftrylockfile(). Они предназначены для использования таким образом, чтобы flockfile(fp); fprintf(fp, "…", …); fprintf(fp, "…", …); funlockfile(fp); сохранял вывод двух последовательных операций fprintf() без возможности для другого потока вставить свой вывод. - person Jonathan Leffler; 11.07.2018

Ничто не будет более эффективным, чем:

  1. Убедитесь, что ваша строка может вместить на один символ больше
  2. Присвоить \n строке
  3. fputs строка
person 0___________    schedule 11.07.2018