Напечатайте последние 10 строк файла или стандартного ввода с помощью read write и lseek

Я работаю над реализацией функции хвоста, и я должен только использовать read(), write() а также lseek() для ввода / вывода, и до сих пор у меня есть это:

int printFileLines(int fileDesc)
{
    char c; 
    int lineCount = 0, charCount = 0;   
    int pos = 0, rState;
    while(pos != -1 && lineCount < 10)
    {
        if((rState = read(fileDesc, &c, 1)) < 0)
        {
            perror("read:");
        }
        else if(rState == 0) break;
        else
        {
            if(pos == -1)
            {
                pos = lseek(fileDesc, 0, SEEK_END);
            }
            pos--;
            pos=lseek(fileDesc, pos, SEEK_SET); 
            if (c == '\n')
            {
                lineCount++;
            }
            charCount++;
        }
    }

    if (lineCount >= 10)
        lseek(fileDesc, 2, SEEK_CUR);
    else
        lseek(fileDesc, 0, SEEK_SET);

    char *lines = malloc(charCount - 1 * sizeof(char));

    read(fileDesc, lines, charCount);
    lines[charCount - 1] = 10;
    write(STDOUT_FILENO, lines, charCount);

    return 0;
}

Пока он работает для файлов, которые имеют более 10 строк, но он тормозит, когда я передаю файл с менее чем 10 строками, он просто печатает последнюю строку этого файла, и я не могу заставить его работать с stdin, Если кто-то может дать мне представление, как решить эту проблему, это было бы здорово:D

1 ответ

Решение

Первый выпуск:

Если вы читаете новую строку здесь...

if(read(fileDesc, &c, 1) < 0)
{
    perror("read:");
}

... и затем установите позицию непосредственно на символ, предшествующий этой новой строке...

pos--;
pos=lseek(fileDesc, pos, SEEK_SET);

а затем linecount является >= 10 (цикл while завершается), затем первый прочитанный вами символ является последним символом строки, предшествующей последней новой строке. Сама новая строка также не является частью последних 10 строк, поэтому просто пропустите два символа из текущей позиции потока:

if (linecount >= 10)
    lseek(fileDesc, 2, SEEK_CUR);

Для второго выпуска:

Предположим, что смещение потока достигло начала потока:

pos--;
pos=lseek(fileDesc, pos, SEEK_SET); // pos is now 0

Условие while все еще TRUE:

while(pos != -1 && lineCount < 10)

Теперь чар читается. После этого смещение файла равно 1 (второй символ):

if(read(fileDesc, &c, 1) < 0)
{
    perror("read:");
}

Здесь pos падает до -1 и lseek завершится ошибкой:

pos--;
pos=lseek(fileDesc, pos, SEEK_SET); 

Поскольку lseek не удалось, позиция в файле теперь является вторым символом, следовательно, первый символ отсутствует. Исправьте это, сбросив смещение файла к началу файла, если pos == -1 после цикла while:

if (linecount >= 10)
    lseek(fileDesc, 2, SEEK_CUR);
else
    lseek(fileDesc, 0, SEEK_SET);

Спектакль:

Для этого нужно очень много системных вызовов. Простым улучшением было бы использование буферизованных f*-функций:

FILE *f = fdopen(fileDesc, "r");
fseek(...);
fgetc(...);

и т.д. Кроме того, для этого не нужны системные функции.

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

Для Unix вы также можете mmap() весь файл и поиск в памяти назад символов новой строки.

Другие вопросы по тегам