LinuxForum.Fans
Тема

Помогите реализовать блочный ввод-вывод

Автор: Dorif 16 сообщений 2291 просмотров
#124421

Код:

text
#define _LARGEFILE64_SOURCE
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h> 
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include "getopt.h"
#define block_size 4096
struct stat fs;
char* itoa(unsigned int val);
void dchk(int f);
int ferr();
int usage();
int main(int argc, char** argv){
    register int ch, filenum;//filenum- FILE NUMber;
    int longIndex;
    register short sht=0, shn=0, shcc=0, num=0, num_nonblank=0;
    //sht- SHow Tabulation; shn- SHow '\n' symbol(carriage return); shcc- SHow Control Characters; num- NUMber;
    static const struct option longOpts[] = {
    { "show-nonprinting", no_argument, NULL, 'v' },
    { "show-tabs", no_argument, NULL, 'T' },
    { "show-ends", no_argument, NULL, 'E' },
    { "show-all", no_argument, NULL, 'A' },
    { "number", no_argument, NULL, 'n' },
    { "version", no_argument, NULL, 0 },
    { NULL, no_argument, NULL, 0 }
    };
    while((ch=getopt_long(argc, argv, "ETAethuni",longOpts,&longIndex))!= -1)
    {
        switch (ch) 
        {
            case 'n':
                num = 1;
                break;
            case 'i':
                num_nonblank = 1;
                break;
            case 'E':
                shn = 1;
                break;
            case 'T':
                sht=1;
                break;
            case 'A':
            {
                shn=1;
                sht=1;
                shcc=1;
                break;
            }
            case 'v':
                   shcc=1;
                   break;
            case 'e':
            {
                   shcc=1;
                   shn=1;
                   break;
            }
            case 't':
            {
                   shcc=1;
                   sht=1;
                   break;
            }
            case '?':
            case 'h':
                usage();
            case 'u':
                break;
            case 0:
            if(strcmp( "version",longOpts[longIndex].name)==0)
            {
                write(STDOUT_FILENO,"sysccat version 0.1\n",20);
                _exit(0);
            }
            else usage();
        }
    }
    argv+=optind;
    argc-=optind;
    if(argc==0)usage();
    if(!sht&&!shn&&!shcc&&!num)for(filenum=0;filenum<argc;++filenum)
    {
        int fd=open(argv[filenum],O_RDONLY);
        if(fd<0)ferr();
        fstat(fd,&fs);
        dchk(fd);
        if(S_ISCHR(fs.st_mode)||S_ISSOCK(fs.st_mode)||S_ISFIFO(fs.st_mode))
        {
            char c;
            while(read(fd,&c,1)!=0)write(STDOUT_FILENO,&c,1);
        }
        else
        {
            off_t filesize=lseek(fd,0,SEEK_END);
            lseek(fd,0,SEEK_SET);
            char c[block_size];
            while(block_size<=filesize){
                read(fd,&c,block_size);
                write(STDOUT_FILENO,&c,block_size);
                filesize-=block_size;
            }
            read(fd,&c,filesize);
            write(STDOUT_FILENO,&c,filesize);
            close(fd);
        }
    }
    else for(filenum=0;filenum<argc;++filenum)
    {
        char a;
        int fd=open(argv[filenum],O_RDONLY);
        if(fd<0)ferr();
        dchk(fd);
        if(num)write(STDOUT_FILENO,"1: ",3);
        register int numline=1;//numline- NUMber of Line
        while(read(fd,&a,1)>0)
        {
            if(num&&a=='\n')
            {
                if(read(fd,&a,1)==0)
                {
                    if(shn)write(STDOUT_FILENO,"$",1);
                    write(STDOUT_FILENO,&a,1);
                }
            else
            {
                if(shn)write(STDOUT_FILENO,"$",1);
                write(STDOUT_FILENO,"\n",1);
                ++numline;
                write(STDOUT_FILENO,itoa(numline),strlen(itoa(numline)));
                write(STDOUT_FILENO,": ",2);
                lseek(fd, -1, SEEK_CUR);
            }
        }
        else if(sht&&a=='\t')write(STDOUT_FILENO,"^I",2);
        else if(shn&&a=='\n')write(STDOUT_FILENO,"$\n",2);
        else if(shcc&&iscntrl(a)&&a!='\n'&&a!='\t')
        {
            a+=64;
            write(STDOUT_FILENO,"^",1);
            write(STDOUT_FILENO,&a,1);
        }
        else write(STDOUT_FILENO,&a,1);
    }
    close(fd);
    }
    _exit(0);
}
int usage(){
    write(STDOUT_FILENO,"Usage: sysccat [OPTIONS]... [FILE]...\n"
    "-h or --help Displays this info and exit.\n"
    "-n or --number Numbers the output strings.\n"
    "--version Displays version and exit.\n"
    "-v or --show-nonprinting Displays non-printing(control) characters, except for tabulation and carriage return/end-of-line symbols.\n"
    "-A, -vET or --show-all Displays all control characters.\n"
    "-t or -vT Displays all control characters, except for carriage return/end-of-line symbol.\n"
    "-u Ignored; for POSIX compatibility.\n"
    "-e or -vE Displays all control characters, except for tabulation symbol.\n",547);
    _exit(0);
    }
char* itoa(unsigned int val){
    static char buf[11];
    short i=10;
    for(;val&&i;--i,val/=10)buf[i]=val%10+48;
    return &buf[i+1];
}
int ferr(){
    write(STDERR_FILENO,strerror(errno),strlen(strerror(errno)));
    write(STDERR_FILENO,".\n",2);
    _exit(1);
}
void dchk(int f){
    if(S_ISDIR(fs.st_mode)){
        write(STDERR_FILENO,"It's a directory.\n",18);
        close(f);
        _exit(1);
        }
}

Блочный ввод-вывод нужно реализовать в части, начинающейся после //if any option defined Пока ничего путного в голову не пришло- бред один только лезет. Заранее спасибо.
P.S.: #include "getopt.h" ибо у меня свой getopt_long- не требующий #define _GNU_SOURCE, базирующийся на коде этой функции из Open и Net BSD.

#125326

Студент? Курсач горит?

А ввести консультантов-помощников в курс Вашего дела (в контекст Вашей проблемы) что -- уже в лом?
Вряд ли кто-то будет разбираться с Вашими шарадами?

С первого взгляда выглядит как: "Я тут из тырнета накопипастил 100 строк кода. Как он работает, я не въезжаю. Сделайте мне красиво!"
Ну и как Вы думаете, кто на такой Ваш призыв о помощи откликнется?

Если хотите чтобы Вам помогли, потрудитесь описать более подробно Вашу задачу.

#125781
zhevak написал:
Студент? Курсач горит?

А ввести консультантов-помощников в курс Вашего дела (в контекст Вашей проблемы) что -- уже в лом?
Вряд ли кто-то будет разбираться с Вашими шарадами?

С первого взгляда выглядит как: "Я тут из тырнета накопипастил 100 строк кода. Как он работает, я не въезжаю. Сделайте мне красиво!"
Ну и как Вы думаете, кто на такой Ваш призыв о помощи откликнется?

Если хотите чтобы Вам помогли, потрудитесь описать более подробно Вашу задачу.

Если вы найдёте в тырнете этот код- будет весьма интересно. А вообще- то это мой самописный клон Cat(и для кого комментарии в коде?). И не 100 строк. а, как говорит Geany- 207 с учётом eof и копирайта(сюда не вписывал). Как работает cat- man cat, подробнее- info cat invocation. А блочный ввод- вывод нужен для ускорения работы программы за счёт того. что инфа будет читаться не посимвольно. а блоками по 4096 байт. что является самым распространённым размером кластера файловой системы, а следовательно использовать данный размер блока рациональнее всего. Вы что, всех подозреваете в том. что они- ленивые студенты? Факт- ко мне понятие "ленивый студент" не применимо ибо: а) я школьник, б) это- одна из моих первых программ.

#125832

Ещё можно получить размер страницы с помощью getpagesize(),
а также из заголовочного файла ядра include/asm-generic/page.h,
константа PAGE_SIZE.

Строки //if any option в приведённом тексте нет, но всё равно
вашу задачу за вас делать не будут. Тут могут ответить на конкретный
вопрос. Вообще не только в дисковый файл можно писать или читать
из него поблочно, но и в файл с последовательным доступом можно
одной командой write отправлять сразу блок байтов, или ожидать прихода
такого блока. При этом размер блока реально имеет смысл делать не больше
программного буфера такого файла.

Пётр.

#125860
Peter написал:
Ещё можно получить размер страницы с помощью getpagesize(),
а также из заголовочного файла ядра include/asm-generic/page.h,
константа PAGE_SIZE.

Строки //if any option в приведённом тексте нет, но всё равно
вашу задачу за вас делать не будут. Тут могут ответить на конкретный
вопрос. Вообще не только в дисковый файл можно писать или читать
из него поблочно, но и в файл с последовательным доступом можно
одной командой write отправлять сразу блок байтов, или ожидать прихода
такого блока. При этом размер блока реально имеет смысл делать не больше
программного буфера такого файла.

Пётр.

Если я правильно понимаю. то значение, которое вернёт getpagesize()- это не размер кластера ФС. а размер страницы в памяти, а читаем- то мы с жёсткого диска. а на нём- ФС. Посмотрел код. который здесь- да. однако здесь этого комментария нет... Странно.... Но вообще- то я имел в виду эту часть кода:

text
else for(filenum=0;filenum<argc;++filenum)
    {
        char a;
        int fd=open(argv[filenum],O_RDONLY);
        if(fd<0)ferr();
        dchk(fd);
        if(num)write(STDOUT_FILENO,"1: ",3);
        register int numline=1;//numline- NUMber of Line
        while(read(fd,&a,1)>0)
        {
            if(num&&a=='\n')
            {
                if(read(fd,&a,1)==0)
                {
                    if(shn)write(STDOUT_FILENO,"$",1);
                    write(STDOUT_FILENO,&a,1);
                }
            else
            {
                if(shn)write(STDOUT_FILENO,"$",1);
                write(STDOUT_FILENO,"\n",1);
                ++numline;
                write(STDOUT_FILENO,itoa(numline),strlen(itoa(numline)));
                write(STDOUT_FILENO,": ",2);
                lseek(fd, -1, SEEK_CUR);
            }
        }
        else if(sht&&a=='\t')write(STDOUT_FILENO,"^I",2);
        else if(shn&&a=='\n')write(STDOUT_FILENO,"$\n",2);
        else if(shcc&&iscntrl(a)&&a!='\n'&&a!='\t')
        {
            a+=64;
            write(STDOUT_FILENO,"^",1);
            write(STDOUT_FILENO,&a,1);
        }
        else write(STDOUT_FILENO,&a,1);
    }
    close(fd);
    }

И да: отправлять- то в файл с последовательной записью блок данных можно. но читать из него блоками нельзя, поэтому у меня для случая. когда никакие опции программе не переданы реализована проверка типа файла и если это- поток FIFO, символьное устройство или сокет- использовать последовательный ввод- вывод. Если не можете помочь с кодом- подскажите хотя бы алгоритм. а то у меня реально мозг ничего путного не выдаёт.

#125893

Ещё размер страницы возвращает sysconf.
Посмотрите также "man mke2fs" и "man dumpe2fs", там есть параметр
blocks-per-group, не знаю сейчас, что это.
Однако на моём разделе команда

text
 /sbin/dumpe2fs -h /dev/sdb5

сообщила, что размер блока - 4096 байтов, а число блоков - 48241179,
что при перемножении даёт именно размер раздела в байтах.

Можно и читать из COM-порта командой read не по одному, а сразу
помногу байтов.

Я не хочу вникать в ваш код, сформулируйте вопрос - что надо
получить или в чём проблема, тогда вам смогут ответить.
Наконец, воспользуйтесь отладочной печатью. Вообще не знаю,
какие трудности могут быть с вводом/выводом.

Пётр.

#125909
Peter написал:

Я не хочу вникать в ваш код, сформулируйте вопрос - что надо
получить или в чём проблема, тогда вам смогут ответить.
Наконец, воспользуйтесь отладочной печатью. Вообще не знаю,
какие трудности могут быть с вводом/выводом.

Пётр.

Нужно: реализовать в приведённом мною фрагменте кода чтение блоками по 4096 байт и корректную обработку этих блоков. А весь приведённый мною код сейчас работает корректно. Просто без блочного ввода-вывода (т.е. без ошибок). Пока просто не могу придумать как это реализовать. А как из порта читать блоками? Описать можете? И как отладочную печать юзать?

#125924

Когда мне было известно, сколько байтов придёт из COM-порта,
то я и указывал это число в команде read, а позле выхода из неё
проверял timeout и признак конца посылки, т.е. что последний
пришедший байт таков, как требуется.

Чтобы считывать блоками, надо знать, структуру и состояние файла,
сколько из него в данный момент м.б. считано и это число и указывать
read, а затем анализировать содержимое буфера, в который считано.
Не вижу, что здесь может вызывать затруднения. Как автор программы,
вы должны занть, что предстваляет собой этот файл.

Вопрос об отладочной печати вообще меня удивил.
Вы не знаете о существовании семейства printf?

Пётр.

#125934
Peter написал:
Когда мне было известно, сколько байтов придёт из COM-порта,
то я и указывал это число в команде read, а позле выхода из неё
проверял timeout и признак конца посылки, т.е. что последний
пришедший байт таков, как требуется.

Чтобы считывать блоками, надо знать, структуру и состояние файла,
сколько из него в данный момент м.б. считано и это число и указывать
read, а затем анализировать содержимое буфера, в который считано.
Не вижу, что здесь может вызывать затруднения. Как автор программы,
вы должны занть, что предстваляет собой этот файл.

Вопрос об отладочной печати вообще меня удивил.
Вы не знаете о существовании семейства printf?

Пётр.

А, я просто про другое подумал при слове "отладочная печать2.) В том- то и дело, что файл- неизвестен. Ни размер. ни структура. ни тип. Я вообще понятия не имею, что это может оказаться за файл. Поэтому ни одной его характеристики предугадать невозможно и нужно делать всё "для сферического файла в вакууме".

#125935

Если размер файла заранее неизвестен, то его можно получить
с помощью вызова lseek, man lseek.
Если уж сосем неизвесте, то при побайтовм чтении надо проверять и признак
конца файла, EOF.

Пётр.

#125963
Peter написал:
Если размер файла заранее неизвестен, то его можно получить
с помощью вызова lseek, man lseek.
Если уж сосем неизвесте, то при побайтовм чтении надо проверять и признак
конца файла, EOF.

Пётр.

Это делается. Тут другая проблема. GNU Cat при нумерации строк не считает последний(т.е. стоящий перед EOF) \n, я пока не могу сообразить. как отличить Самый Последний Конец Строки от всех прочих при блочном i/o.

#126063
Dorif написал:
[quote=Peter]Если размер файла заранее неизвестен, то его можно получить
с помощью вызова lseek, man lseek.
Если уж сосем неизвесте, то при побайтовм чтении надо проверять и признак
конца файла, EOF.

Пётр.

Это делается. Тут другая проблема. GNU Cat при нумерации строк не считает последний(т.е. стоящий перед EOF) \n, я пока не могу сообразить. как отличить Самый Последний Конец Строки от всех прочих при блочном i/o.[/quote]
Думаю, что по признаку конца файла. Если при блочном чтении считалось меньше,
чем было указано, значит, конец уже достигнут и последняя строка считана. read вернёт
число считанных байтов меньшее, чем было задано. Если конец последнего блока совпал
с концом файла, то для проверки этого можно ещё рез сделать считывание, если будет
ошибка из-за достижения конца файла, то имеем прдыдущий случай.

Пётр.

#126334
Peter написал:
[quote=Dorif][quote=Peter]Если размер файла заранее неизвестен, то его можно получить
с помощью вызова lseek, man lseek.
Если уж сосем неизвесте, то при побайтовм чтении надо проверять и признак
конца файла, EOF.

Пётр.

Это делается. Тут другая проблема. GNU Cat при нумерации строк не считает последний(т.е. стоящий перед EOF) \n, я пока не могу сообразить. как отличить Самый Последний Конец Строки от всех прочих при блочном i/o.[/quote]
Думаю, что по признаку конца файла. Если при блочном чтении считалось меньше,
чем было указано, значит, конец уже достигнут и последняя строка считана. read вернёт
число считанных байтов меньшее, чем было задано. Если конец последнего блока совпал
с концом файла, то для проверки этого можно ещё рез сделать считывание, если будет
ошибка из-за достижения конца файла, то имеем прдыдущий случай.

Пётр.[/quote]
А если количество символов кратно 4096? Тогда не получится. Последний конец строки будет выведен. потом будет выведен номер, а потом только прога сообразит, чсто файл закончился.

#126338
Dorif написал:
Тут другая проблема. GNU Cat при нумерации строк не считает последний(т.е. стоящий перед EOF) \n, я пока не могу сообразить. как отличить Самый Последний Конец Строки от всех прочих при блочном i/o.

дык \n входит в строчку. если 10 строк, то 10 штук \n.

#126362

Что-то типа

text

#define BUFFER_SIZE 1024 ... char buffer[BUFFER_SIZE];

size_t count = 0; while(count = read(дескриптор, buffer, BUFFER_SIZE)) { if(count == BUFFER_SIZE) { // прочитали BUFFER_SIZE байт файл не пуст }else if(count < BUFFER_SIZE ) { // прочитали count байт, файл пуст } }

#126526
man 2 read написал:
DESCRIPTION
read() attempts to read up to count bytes from file descriptor fd into the
buffer starting at buf.

If count is zero, read() returns zero and has no other results. If count is
greater than SSIZE_MAX, the result is unspecified.

RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file),
and the file position is advanced by this number. It is not an error if this
number is smaller than the number of bytes requested; this may happen for exam-
ple because fewer bytes are actually available right now (maybe because we were
close to end-of-file, or because we are reading from a pipe, or from a termi-
nal), or because read() was interrupted by a signal. On error, -1 is returned,
and errno is set appropriately. In this case it is left unspecified whether the
file position (if any) changes.

вот что тут непонятного?

Войдите или зарегистрируйтесь, чтобы ответить.