2016-10-08 3 views
0

Я пытаюсь прочитать файл неизвестного размера по строке, включая один или несколько символов новой строки. , например, если мой sample.txt файл выглядит следующим образомчтение файла строки за строкой, включая несколько символов новой строки

abc cd er dj 
text 

more text 


zxc cnvx 

Я хочу, чтобы мои строки, чтобы выглядеть как этот

string1 = "abc cd er dj\n"; 
string2 = "text\n\n"; 
string3 = "more text\n\n\n"; 
string4 = "zxc convex"; 

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

while((temp = fgetc(input)) != EOF) { 
    if (temp != '\n') { 
     length++; 
    } 
    else { 
     if (temp == '\n') { 
      while ((temp = fgetc(input)) == '\n') { 
       length++; 
      } 
     } 
     length = 0; 
    } 
} 

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

Я также не хочу использовать буфер, потому что я не знаю длины каждой строки. Любая помощь будет оценена.

+0

Чтобы получить длину каждой строки. Если я этого не сделаю, он будет продолжать добавлять длину. –

+0

Выделите память для строки. Прочитайте файл с 'fgets'. Если в конце строки нет символа 'newline' (и вы не находитесь в конце файла), вся строка не была прочитана, поэтому вы перераспределяете более длинную строку и продолжаете чтение в конце предыдущие данные. –

+0

линии не имеют среднего размера. Некоторые строки могут быть слишком большими, а некоторые могут быть слишком маленькими. Я также не знаю, как большой файл будет. Он может состоять из нескольких строк или 16 000 строк. –

ответ

1

Если строки короткие, их не так много, вы можете использовать realloc для перераспределения памяти по мере необходимости. Или вы можете использовать меньшие (или более крупные) куски и перераспределять. Это немного более расточительно, но, надеюсь, в конечном итоге это должно усреднить.

Если вы хотите использовать только одно выделение, то найдите начало следующей непустой строки и сохраните позицию файла (используйте ftell). Затем получите разницу между текущей позицией и предыдущей стартовой позицией, и вы знаете, сколько памяти будет выделено. Для чтения да, вам нужно искать назад и вперед, но если он не большой, все данные будут в буфере, чтобы просто изменить некоторые указатели. После прочтения затем найдите сохраненную позицию и сделайте ее следующей стартовой позицией.

Тогда вы, конечно, могли бы memory-map the file. Это поместит содержимое файла в вашу карту памяти, как будто все было выделено. Для 64-битной системы адресное пространство достаточно велико, чтобы вы могли сопоставлять файлы с несколькими гигабайтами. Тогда вам не нужно искать или выделять память, все, что вы делаете, это манипулировать указателями вместо поиска. Чтение - просто просто копирование памяти (но тогда, поскольку файл находится в «памяти», вам это действительно не нужно, просто сохраните указатели).


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

#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    FILE *file = fopen("some_text_file.txt", "r"); 

    // The position after a successful open call is always zero 
    long start_of_line = 0; 

    int ch; 

    // Read characters until we reach the end of the file or there is an error 
    while ((ch = fgetc(file)) != EOF) 
    { 
     // Hit the *first* newline (which differs from your problem) 
     if (ch == '\n') 
     { 
      // Found the first newline, get the current position 
      // Note that the current position is the position *after* the newly read newline 
      long current_position = ftell(file); 

      // Allocate enough memory for the whole line, including newline 
      size_t bytes_in_line = current_position - start_of_line; 
      char *current_line = malloc(bytes_in_line + 1); // +1 for the string terminator 

      // Now seek back to the start of the line 
      fseek(file, start_of_line, SEEK_SET); // SEEK_SET means the offset is from the beginning of the file 

      // And read the line into the buffer we just allocated 
      fread(current_line, 1, bytes_in_line, file); 

      // Terminate the string 
      current_line[bytes_in_line] = '\0'; 

      // At this point, if everything went well, the file position is 
      // back at current_position, because the fread call advanced the position 
      // This position is the start of the next line, so we use it 
      start_of_line = current_position; 

      // Then do something with the line... 
      printf("Read a line: %s", current_line); 

      // Finally free the memory we allocated 
      free(current_line); 
     } 

     // Continue loop reading character, to read the next line 
    } 

    // Did we hit end of the file, or an error? 
    if (feof(file)) 
    { 
     // End of the file it is 

     // Now here's the tricky bit. Because files doesn't have to terminated 
     // with a newline, at this point we could actually have some data we 
     // haven't read. That means we have to do the whole thing above with 
     // the allocation, seeking and reading *again* 

     // This is a good reason to extract that code into its own function so 
     // you don't have to repeat it 

     // I will not repeat the code my self. Creating a function containing it 
     // and calling it is left as an exercise 
    } 

    fclose(file); 

    return 0; 
} 

Обратите внимание, что для краткости программа не содержит обработки ошибок. Следует также отметить, что я на самом деле не пробовал программу, даже не пытался ее скомпилировать. Для этого ответа все написано специально.

+0

Будет ли это работать, даже если файл очень большой? –

+0

@samadbond Это зависит от многого. Вам нужно постоянно держать * все * строки в памяти? Что вы должны делать с данными, которые вы читаете? О, и я отредактировал ответ, чтобы включить третий способ, который вам не нужен ни для поиска, ни для (активной) памяти. –

+0

Я добавлю эти строки в свой список. Я пытаюсь преобразовать простой текст в текст html, и причина, по которой я пытаюсь читать новые строки, заключается в том, что html имеет теги абзацев –

0

Вы получаете неправильную длину.Причина заключается в том, что перед тем, как войти в цикл:

while ((temp = fgetc(input)) == '\n') 

Вы забыли, чтобы увеличить length, как это только что прочитал \n характер. Поэтому эти строки должны стать:

else { 
    length++;    // add the \n just read 
    if (temp == '\n') {  // this is a redundant check 
     while ((temp = fgetc(input)) == '\n') { 
      length++; 
     } 
     ungetc(temp, input); 
    } 


EDIT

После прочтения первого не \n, теперь вы прочитали первый символ следующей строки, так что вы должны уничтожать в зародыше его:

ungetc(temp, input); 
+0

только фиксирует длину первой строки. Длина остальных строк по-прежнему неверна. –

+0

Надеюсь, мое последнее исправление исправлено. К сожалению, я не могу проверить это в этот момент. –

+0

Теперь он работает! спасибо –

0

Если вы не пытаетесь написать свою собственную реализацию, вы можете использовать the standard POSIX getline() function:

#include <stdio.h> 
#include <stdlib.h> 


int main(void) 
{ 
    FILE *fp; 
    char *line = NULL; 
    size_t len = 0; 
    ssize_t read; 
    fp = fopen("/etc/motd", "r"); 
    if (fp == NULL) 
     exit(1); 
    while ((read = getline(&line, &len, fp)) != -1) { 
     printf("Retrieved line of length %zu :\n", read); 
     printf("%s", line); 
    } 
    if (ferror(fp)) { 
     /* handle error */ 
    } 
    free(line); 
    fclose(fp); 
    return 0; 
} 
+0

Спасибо за ответ, но ваше решение не решило мою проблему. Я также пытаюсь читать новые строки. –

+0

@samadbond * Спасибо за ответ, но ваше решение не решает мою проблему. Я также пытаюсь читать строки новой строки. * Что заставляет вас думать, что 'getline()' не читает символы новой строки? Вы * прочитали * документацию для 'getline()'? В нем говорится: «После успешного завершения функции getline() и getdelim() возвращают количество байтов, записанных в буфер, ** включая символ разделителя, если он был встречен перед EOF **». –

+0

Он включает только один новый символ линии. Когда я запускал ваш код с моим файлом sample.txt, он печатает длину «text» 5, а не 6 –

Смежные вопросы