LINUX.ORG.RU

Ввод-вывод. fgetc, getc, fileno.


0

1

[СИ] Ввод-вывод. fgetc, getc, fileno.

Язык СИ
ОС UNIX

Вопрос-1.
В каких случаях следует или не следует применять getc вместо fgetc?

Например, СИ-функция общего назначения, т. е. заранее неизвестно где она
будет применяться - возможно в много-потоковой, или иной программе
с особенностями.
Предполагается чтение файла библиотечными getc (или fgetc), fread, rewind.
Быстродействие желательно. В чем опасность getc, что она выделена отдельно?

Вопрос-2.
Есть СИ-функция, в которую передается fd, и которая читает файл read(fd).
А нужна такая же, но чтоб фигурировал аргумент fp, а не fd.
Переделать ее на бибиотечную fread() трудно, и будет двойное
буферирование.
Для нее я сделал обертку, т. к. файл открывается функцией fopen().
Ниже приведена схема.


int fun(int fd)
{
    int k;
    k=read(fd);
    if(k<0) return(-1);
    // обработка файла
    return(0);
}

int obertka_fun(FILE *fp)
{
    int fd, k;
    fd=fileno(fp);
    k=fun(fd);
    return(k);
}

int main()
{
    FILE *fp=NULL;
    int k;
    // этот цикл символизирует бесконечность процесса
    while(1){
        // получить имя файла
        fp=fopen(fname, "r");
        if(fp==NULL) continue;
        k=obertka_fun(fp);
        fclose(fp);
    }
    exit(0);
}

В чем тут опасность? Осталось ли что-нибудь незакрыто?

Кто знает прошу ответить.


1. fgetc и getc суть одно и то же
2. fileno должно хватить

3. Что такое «двойное буферирование» я не понял. По-русски можно?

anonymous
()

> В каких случаях следует или не следует применять getc вместо fgetc?

Единственное отличие getc от fgetc - она может быть реализована, как макро. Следствие из этого очевидно - аргумент FILE *stream будет вычисляться многократно в сложных конструкциях => рост накладных расходов.

Поэтому имхо лучше применять fgetc.

OldFatMan
()
Ответ на: комментарий от OldFatMan

В мане написано, что getc может быть реализовано через
макрос. Мне нет дела, как оно будет реализовано, лишь бы
работало всегда. И я подумал, что через макрос будет быстрее.
Но как бы чего не вышло.

«двойное буферирование» означает вот что.
Эту фyкцию я написал. И сделал там самодельное буферирование.
Всегда читается по 512 байт, а далее как хочу, так и ворочу.
Когда понадобилось fp, то сразу сделал два варианта:
-заменил read на fread (двойное буферирование);
-обертка.

Более правильным вариантом считаю - сразу делать под
fread, fgetc. И не мыркаться. Но функция-то уже написана.
Конкретно GIF-кодер для капчи. А сейчас нужен просто
универсальный GIF-кодер. В принципе, легко переделать
специальный на универсальный. Но только в принципе.
А ошибки, которых в специальном вроде бы нет,
сколько понаделаю ошибок?

Двойное буферирование: библиотечные уже сразу буферированные.
Плюс у меня внутренняя буферизация. Читаю всегда по 512.
А потом из своего буфера по-битно.

OldFatMan
Мне не понятно, что значит
«FILE *stream будет вычисляться многократно».
getc специально придумано что-ли, чтоб вычисляться многократно?
Поясните в двух словах - в чем разница?

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

> getc специально придумано что-ли, чтоб вычисляться многократно?

Вовсе нет. Просто в man-е предупреждают, что getc, в отличие от fgetc, может быть реализован, как макроподстановка. А в коде макроподстановка может создать ситуацию, когда выполняются избыточные операции, определяющие её аргумент - указатель на поток ввода FILE *stream.

А fgetc - гарантированная функция в любом случае. Её и следует использовать. Если getc тоже реализована, как функция, то они с fgetc абсолютно идентичны. Но... зачем рисковать, особенно если производительность критична для приложения?

OldFatMan
()
Ответ на: комментарий от OldFatMan

Если критична производительность, зачем вообще использовать буферизованный ввод-вывод? Есть же read/write…

Eddy_Em ☆☆☆☆☆
()

А зачем вы открываете файл при помощи fopen, если используете только небуферизованный ввод-вывод? Или вы хотите использовать смешанные способы работы с файлом? Если второе, то это в книжках делать не рекомендуют.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Это не у меня надо спрашивать, а у ТСа. Я не знаю, какие у него ограничения на операции ввода/вывода. Он спросил про различия getc и fgetc, вот я и попытался объяснить, как понимаю.

В коде ТСа вообще какая-то каша из буф/небуф ВВ. Но может быть у него условия задачи такие, по-другому нельзя? Не знаю.

OldFatMan
()
Ответ на: комментарий от OldFatMan

В коде ТСа вообще какая-то каша из буф/небуф ВВ

Из буферизованного в его коде только fopen/fclose. А вообще, если он собирается мешать буф/небуф, то получится у него, скорее всего, каша: fread считает кусок файла в буфер, сместив seek, read будет считывать с этого seek'а, затем fread - из своего буфера, если fread'у понадобится подкачать данных - он будет брать их с нового seek'а, оставленного read'ом…

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

А может каши и не будет. Вот сейчас повнимательнее посмотрел.

Обёртка из fp - через fileno - получает fd.

Потом всё чтение идет read-ом (в fun).

Вроде ничего не смешивается.

Настораживает лишь вопрос про getc/fgetc.

Зачем такой изврат нужен - не знаю. Возможно, начальные условия такие были. Бывают такие ситуации, по себе знаю. :)

OldFatMan
()
Ответ на: комментарий от Eddy_Em

И я совсем не понял этого:

Переделать ее на бибиотечную fread() трудно, и будет двойное буферирование.

И зачем ТСу все эти костыли?! Почему fread() не попользовать? Так и не понял.

OldFatMan
()
Ответ на: комментарий от OldFatMan

Ему, скорее всего, в каких-то функциях не нужно использовать буферизацию. А в каких-то он не хочет городить велосипед и желает пользоваться stdio.

Я бы ему посоветовал использовать-таки только небуферизованный ввод-вывод. Иначе придется быть очень осторожным.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Да. Разброд получается и каша.
Задание с fp.
И вновь написанные функции приспособлены для этого.
Непонятно только с getc и fgetc. При опробовании работает и так и так.
Почему-то при опробовании всегда всё работает.
Видимо, придется перейти на fgetc, как более надежной.

В «старых» специальных функциях заложено read(fd).
Не хотелось их переделывать. Эти переделки связаны с большим количеством
ошибок. Лучшие из них выявляются на опробовании. Другие остаются надолго.

Понимаю, что это моя головная боль, не Ваша.
Призываю вернуться к начальным вопросам темы. Особенно второй вопрос.
Он очень конкретный, и ответ хотелось бы получить конкретный.
Всё ли закрыто?

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

> Непонятно только с getc и fgetc. При опробовании работает и так и так.

Так и должно быть. Я же писал, что они практически одинаково работают.

Видимо, придется перейти на fgetc, как более надежной.

Это правильно.

Он очень конкретный, и ответ хотелось бы получить конкретный. Всё ли закрыто?

В начале цикла открываешь файл, потом читаешь из него, потом закрываешь. В идеальном случае после завершения каждой итерации цикла всё закрыто.

Но есть один нюанс. Если внутри бесконечного цикла до вызова fclose() твою программу прервут, например, Ctrl-C, Ctrl-\ или ещё каким-либо способом, то каким образом ты обрабатываешь эту ситуацию? В приведённом коде - никак. А надо бы позаботиться об этом.

OldFatMan
()
Ответ на: комментарий от oleg_2

Нет. Я не хочу использовать смешанный доступ к файлу.
Но задание такое, через fp.
Это и понятно.
Там несколько функций для обработки файла. А именно:
-определить тип файла;
-в зависимости от типа обработать файл одной из имеющихся функций.

С точки зрения людей, пишущих главную программу, чтож им,
по разному открывать файл для той или другой функции?
Должно быть единообразие.
Файл, попавший «в лапы» той, или другой функции,
полностью в распоряжении этой функции. Смешанного чтения
read/fread не будет.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

Да. В приведенном коде никак не обрабатываю.
Здесь моя позиция такова (и с руководителем обсуждалась)
пусть люди, которые пишут главную программу, позаботятся
об обработке сигналов.
Когда я писал небольшие, но цельные программы, я сам и заботился
о надлежащей блокировке сигналов, и об их обработке.
Сигналов много, откуда мне зннать, какие ожидаются.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

Все-таки, объясните, пожалуйста: если вам нужно работать с небуферизованным вводом/выводом, зачем вы открываете файл через fopen, а не open?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Файл не я открываю. Я пишу только функцию, а не всю программу.
Задано, что бы эта функция принимала аргумент fp.
Это потому, что так удобнее для написания главной программы.
Есть несколько функций обработки файла.
«Они», кто пишет главную, открывают файл,
определяют его тип с помощью функции определения типа (передают в нее fp),
в зависимости от типа вызывают одну из функций обработки файла (передают в нее fp).
Удобно, чтоб раз уж открыли fopen, то с ним и работать, т. е. все функции
должны принимать fp.
Они и закроют файл после обработки.
Функция main на схеме в начале темы - это главная программа.

Одна функция была написана ранее для другой цели, я же и написал.
И в ней внутренняя буферизация и read. Вот для нее обертка с переходом
от fp на fd.
Если fileno() просто возвращает номер из cтруктуры FILE, и ничего больше
не делает, то как будто ничего страшного нет.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

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

Опишу интересный, на мой взгляд, опыт.
Сделал я функцию обработки файла, тестовую, с распечаткой. Чтение fread.
Поместил ее в цикл с sleep(10), чтобы между вызовами менять файл на другой
с этим же именем.
Но сделал только один fopen до цикла, а в цикле rewind.
Запустил и стал во время sleep(10) менять входной файл.
Когда файл попадался большой, то работало как ожидал.
А если файл маленький, видимо целиком входящий в буфер стандартного ввода,
то файл не перечитывался, и обрабатывался не тот файл, который я подсунул,
а старый отавшийся в буфере.
Очевидно, rewind() не гарантирует обязательную перечитку.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

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

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

В сообщении OldFatMan был вопрос:
«Настораживает лишь вопрос про getc/fgetc.
Зачем такой изврат нужен - не знаю.»
который я как-то пропустил незамеченным.

Вот функция побитового чтения файла ():

//---------------------- jp_get_bit ------------------

// взять один бит

int jp_get_bit(FILE *fp, struct jp *jp)

{
    int k;
    struct get_bit_cod *s;

    s = &jp->get_bit_cod;
    if(s->bit_count-- <= 0){
        s->ch = getc(fp);
        if(s->ch == EOF){ jp->err = JPD_ERR_RD_FILE; return(-1);}
        if(s->ch == 0xFF){
            k=getc(fp);
            if(k==EOF){ jp->err = JPD_ERR_RD_FILE; return(-1);}
            if(k!=0){
                jp->marker = k;
                jp->err = JPD_MARKER_IN_SCAN;
                return(-1);
            }
        }
        s->bit_count=7;
    }
    return((((s->ch)<<=1)&0x100)>>8);
}

Чтение через getc (или fgetc).
Я тут ничего плохого не вижу.
Можно сделать свой буфер и читать блоками read.
Может это будет чуть быстрее, но программа или усложнится в написании
(в этой и еще в других функциях добавится счетчик байт), или вместо
getc(fp), написать свою облегченную my_getc(fp).

Если завтра скажут, что аргумент должен быть fd, то нетрудно написать
свои облегченные my_getc, my_fread.
Если завтра скажут, что читать вообще не надо, а будет аргументы
const char *bufer, int lenbufer,
и в буфере будет весь входной файл,
то нетрудно написать аналоги my_getc, my_fread для буфера.

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

oleg_2
() автор топика
Ответ на: комментарий от anonymous

Да.
Считаю даже, что уже написал, за мелкими
недоделками. Нет не то, чтобы тестовых,
просто пробных файлов. Ищу в «Яндекс картинках» и
так по сайтам.
Не нашел ни одного с рестартом. Рестарт встроил наудачу,
по аналогии с базовым способом (базовые с рестартом есть,
а делаю прогрессивные).
Опробовать нечем.
Бывают еще с маркером конца строки. Пока без
обработки этого маркера (не нашел, разумеется).
Прошу разработчиков главной программы, чтобы
файлы, выдавшие ошибку, как-то сохранять. Но
надежда слабая. Не будут они такой ерундой забивать
голову.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

> Не нашел ни одного с рестартом.

я для тестирования своего использовал jpegtran из libjpeg, генерил им картинки со всеми возможными сочетаниями параметров.

в частности, оно умеет -progressive -restart N.

а что за маркер конца строки?

anonymous
()
Ответ на: комментарий от anonymous

Спасибо.

0xFFDC
DNL
Define number of lines

From second
scan to last,
first scan when
number of lines
defined correctly
in frame header

Как я понял, определяет число «строк» MCU, а на самом деле
«строк» матриц 8х8 по высоте рисунка.

Number of lines – Specifies the maximum number of lines in the source image.
This shall be equal to the
number of lines in the component with the maximum number of vertical samples (see A.1.1).
Value 0 indicates
that the number of lines shall be defined by the DNL marker and parameters at the end
of the first scan

Мне это не понятно. Вторичные сканы (однокомпонентные) и так идут лентой матриц,
не сгруппированных в макроблоки (макроблоком я называю, например, 16х16 пикселей
четыре Y и по одной Cb, Cr), лишних «краевых» матриц не включают.
И смысл этого маркера от меня ускользнул. И без него хорошо.
Без примеров плохо разбираться.

oleg_2
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.