C fopen vs open


220

Есть ли какая-либо причина (кроме синтаксической), которую вы хотели бы использовать

FILE *fdopen(int fd, const char *mode);

или

FILE *fopen(const char *path, const char *mode);

вместо того

int open(const char *pathname, int flags, mode_t mode);

при использовании C в среде Linux?


Вы имели в виду fdopenи openили или fopenи open?
user7116

Разве вы не имеете в виду fopen, а не fdopen?
Всезнающий

9
fopenявляется частью стандартной библиотеки C, openнет. Используйте fopenпри написании переносимого кода.
Азиз

Да, я имел в виду fopen. Я только что обновил его, но я думаю, что тот же принцип применим.
LJM

6
@Aziz, openхотя это функция POSIX.
Dreamlax

Ответы:


244

Во-первых, нет особенно веской причины использовать fdopenif fopenкак вариант, так и openдругой возможный выбор. Вы не должны openбыли открывать файл, если хотите FILE *. Поэтому включение fdopenв этот список неверно и запутанно, потому что оно не очень похоже на другие. Теперь я перейду к его игнорированию, потому что здесь важно различать стандарт C FILE *и дескриптор файла, специфичный для ОС.

Есть четыре основные причины для использования fopenвместо open.

  1. fopenобеспечивает буферизацию ввода-вывода, которая может оказаться намного быстрее, чем то, что вы делаете open.
  2. fopen выполняет перевод строки, если файл не открывается в двоичном режиме, что может быть очень полезно, если ваша программа портирована в не-Unix-среду (хотя кажется, что мир сходится только на LF (кроме текстовой сети IETF) протоколы типа SMTP и HTTP и такие)).
  3. А FILE *дает вам возможность использовать fscanfи другие функции stdio.
  4. Возможно, когда-нибудь ваш код придется перенести на другую платформу, которая поддерживает только ANSI C и не поддерживает эту openфункцию.

На мой взгляд, перевод конца строки чаще встречается у вас на пути, чем помогает, а синтаксический анализ fscanfнастолько слаб, что вы неизбежно в конечном итоге выбрасываете его в пользу чего-то более полезного.

И большинство платформ, поддерживающих C, имеют openфункцию.

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

Если вы выполняете поиск (иначе, fsetposили fseekвторой, который немного сложнее использовать в соответствии со стандартами), полезность буферизации быстро снижается.

Конечно, мой уклон заключается в том, что я склонен много работать с сокетами, и есть тот факт, что вы действительно хотите делать неблокирующий ввод-вывод (который FILE *полностью не поддерживается каким-либо разумным способом) без какой-либо буферизации и часто сложные требования синтаксического анализа действительно окрашивают мое восприятие.


4
Я не собираюсь ставить под сомнение ваш опыт, но я бы хотел услышать, что вы немного подробнее остановитесь на этом. Как вы считаете, для каких приложений мешает встроенная буферизация? в чем именно проблема?:
Эмиль Х

1
Не видел последний абзац. Допустимая точка, ИМХО. Насколько я могу судить, вопрос был о файловом IO.
Эмиль Х

7
Чтобы уточнить, когда мешает буферизация. Это когда вы используете поиск. Следующее чтение с любой командой ( fgets, fgetc, fscanf, fread), всегда будет читать весь размер буфера (4K, 8K или как вы установите). Используя прямой ввод / вывод, вы можете избежать этого. В этом случае даже лучше использовать preadвместо пары поиск / чтение (1 системный вызов вместо 2).
Патрик Шлютер

2
Обработка прерываний read()и write()вызовов является удобной пятой причиной использования семейства функций libc.
nccc

3
@ m-ric: Ну, это несколько не связанный вопрос, но да. Все платформы, которые поддерживают, ioctlтакже поддерживают filenoвызов, который принимает FILE *и возвращает номер, который можно использовать в ioctlвызове. Будьте осторожны, хотя. FILE *связанные вызовы могут неожиданно взаимодействовать с использованием ioctlдля изменения чего-либо в базовом файловом дескрипторе.
Всевышний

54

open()это вызов низкого уровня. fdopen()преобразует файловый дескриптор уровня os в FILE-абстракцию более высокого уровня языка C. fopen()вызывает open()в фоновом режиме и дает вам FILE-указатель напрямую.

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


3
Есть ли недостатки в использовании буферизованных версий 'f ...' open?
LJM

5
@L. Мозер, да, когда вы уже буферизуете данные, и, следовательно, дополнительный буфер добавляет ненужное копирование и накладные расходы памяти.
Майкл Аарон Сафьян

6
на самом деле есть и другие недостатки. fopen()не обеспечивает одинаковый уровень контроля при открытии файлов, например, создает разрешения, режимы совместного использования и многое другое. как правило, open()и варианты обеспечивают гораздо больший контроль, близкий к тому, что на самом деле обеспечивает операционная система
Мэтт Джоинер,

2
Существуют также крайние случаи, когда вы делаете mmapфайл и вносите изменения с помощью обычного ввода-вывода (как бы невероятно это ни звучало на самом деле, как в нашем проекте и по очень веским причинам), буферизация была бы на пути.
Патрик Шлютер

Вы также можете использовать другие системные функции, такие как open () для предварительной загрузки файлов в кеш страниц через readahead (). Я предполагаю, что эмпирическое правило гласит: «используйте fopen, если вам абсолютно не нужен open ()», open () фактически позволяет вам делать причудливые вещи (установка / не установка O_ATIME и т. П.).
Томас Прузина

34

Fopen против открытого в C

1) fopenявляется библиотечной функцией, пока openявляется системным вызовом .

2) fopenобеспечивает буферный ввод - вывод , который быстрее , по сравнению с openкоторым это не буферизацией .

3) fopenявляется переносным, но openне переносимым ( открытый характерен для конкретной среды ).

4) fopenвозвращает указатель на структуру FILE (FILE *) ; openвозвращает целое число, идентифицирующее файл.

5) A FILE *дает вам возможность использовать fscanf и другие функции stdio.


9
openэто стандарт POSIX, так что он вполне переносим
osvein

12

Если вы не являетесь частью 0,1% приложений, где использование openявляется фактическим выигрышем в производительности, на самом деле нет веских причин не использовать его fopen. Что касается fdopenвопроса, если вы не играете с файловыми дескрипторами, вам не нужен этот вызов.

Палка с fopenи его семейство методов ( fwrite, fread, fprintf, и др) , и вы будете очень довольны. Не менее важно, что другие программисты будут довольны вашим кодом.


11

Если у вас есть FILE *, вы можете использовать такие функции , как fscanf, fprintfи fgetsт.д. Если у вас есть только дескриптор файла, вы ограничены (но , вероятно , быстрее) подпрограммы ввода и вывода read, и writeт.д.


7

Использование open, read, write означает, что вам нужно беспокоиться о сигнальных взаимодействиях.

Если вызов был прерван обработчиком сигнала, функции вернут -1 и установят errno в EINTR.

Таким образом, правильный способ закрыть файл будет

while (retval = close(fd), retval == -1 && ernno == EINTR) ;

4
Ведь closeэто зависит от операционной системы. Неправильно выполнять цикл в Linux, AIX и некоторых других операционных системах.
strcat

Кроме того, при использовании чтения и записи возникает та же проблема, т. Е. Они могут быть прерваны сигналом перед полной обработкой ввода / вывода, и программист должен справиться с такими ситуациями, в то время как утилиты fread и fwrite отлично справляются с прерываниями сигнала.
Марсело

6

open()это системный вызов, специфичный для Unix-систем, и он возвращает дескриптор файла. Вы можете записать в дескриптор файла, используя write()другой системный вызов.
fopen()это вызов функции ANSI C, который возвращает указатель на файл и переносим на другие ОС. Мы можем записать в файл указатель, используя fprintf.

В Unix:
Вы можете получить указатель файла из файлового дескриптора, используя:

fP = fdopen(fD, "a");

Вы можете получить дескриптор файла из указателя файла, используя:

fD = fileno (fP);

4

open () будет вызываться в конце каждой из функций семейства fopen () . open () - это системный вызов, а fopen () предоставляются библиотеками в качестве функций-оболочек для простоты использования.


2

Я переключился на open () из fopen () для моего приложения, потому что fopen вызывал двойное чтение каждый раз, когда я запускал fopen fgetc. Двойное чтение мешало тому, чего я пытался достичь. open () просто делает то, о чем вы просите.


2

Зависит также от того, какие флаги требуется открыть. Что касается использования для записи и чтения (и переносимости), следует использовать f *, как указано выше.

Но если в основном вы хотите указать больше, чем стандартные флаги (например, флаги rw и append), вам придется использовать специфичный для платформы API (например, открытый POSIX) или библиотеку, которая абстрагирует эти детали. Стандарт C не имеет таких флагов.

Например, вы можете открыть файл, только если он завершится. Если вы не укажете флаг создания, файл должен существовать. Если вы добавите эксклюзив для создания, он будет создавать файл, только если он не существует. Есть много других.

Например, в системах Linux есть светодиодный интерфейс, предоставляемый через sysfs. Это выставляет яркость ведомого через файл. Запись или чтение числа в виде строки в диапазоне от 0 до 255. Конечно, вы не хотите создавать этот файл и писать в него, только если он существует. Самое интересное: используйте fdopen для чтения / записи этого файла с помощью стандартных вызовов.


0

Открыв файл с помощью fopen,
прежде чем мы сможем прочитать (или записать) информацию из (в) файла на диске, мы должны открыть файл. чтобы открыть файл мы вызвали функцию fopen.

1.firstly it searches on the disk the file to be opened.
2.then it loads the file from the disk into a place in memory called buffer.
3.it sets up a character pointer that points to the first character of the buffer.

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


открыт быстрее, чем fopen?
Обайхан

да, open - это системный вызов, который быстрее, чем fopen - сравнительно @obayhan
prashad
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.