2017-02-01 6 views
2

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

fptr = fopen("file.txt", "r"); 

    fseek(fptr, 0, SEEK_END); 
    count = ftell(fptr); 

    while (i < count) 
    { 
      i++; 
      fseek(fptr, -i, SEEK_END); 
      printf("%c", fgetc(fptr)); 
    } 

    printf("\n"); 
    fclose(fptr); 

Выход пробы из этого будет

Вход:

Hello 
My name is 

Выход:

si eman yM 
olleH 

То, что я хочу выход:

My name is 
Hello 
+0

Что вы хотите, если есть более одной строки? – Schwern

+0

'Меня зовут Hello' не назад от' Hello My name is'. Вы имели в виду 'is name My Hello'? – Schwern

+0

Скажите, что несколько строк - это Hello и мое имя, я хочу, чтобы мое имя было напечатано первым, а следующая строка была Hello, почти так же, как они переключались местами – JsmileyJ

ответ

0

Как вы это делаете, читаете персонажа и ищете назад, неэффективны.

Модуль Perl File::Readbackwards является хорошим для чтения. Perl IO очень близок к C, и код хорошо комментируется.

Основным алгоритмом является чтение и буферизация блоков и поиск строк внутри этого блока, но начиная с конца файла и перехода назад.

  1. Открыть файл.
  2. Ищите до конца.
  3. Ищите назад к предыдущему блоку.
  4. Прочитайте этот блок в буфер.

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


Это нетривиальная, так вот, как вы могли бы сделать это чтение назад один символ за один раз, пока не появится символ новой строки. Я использовал интерфейс fgets для получения одной строки за раз.

char *fgets_backwards(char *str, int size, FILE *fp) { 
    /* Stop if we're at the beginning of the file */ 
    if(ftell(fp) == 0) { 
     return NULL; 
    } 

    int i; 
    /* Be sure not to overflow the string nor read past the start of the file */ 
    for(i = 0; ftell(fp) != 0 && i < size; i++) { 
     /* Back up one character */ 
     fseek(fp, -1, SEEK_CUR); 
     /* Read that character */ 
     str[i] = (char)fgetc(fp); 

     /* We have the whole line if we see a newline, except at the start. 
      This happens before we back up a character so the newline will 
      appear on the next line. */ 
     if(str[i] == '\n' && i != 0) { 
      break; 
     } 

     /* Back up the character we read. */ 
     fseek(fp, -1, SEEK_CUR); 
    } 

    /* Null terminate, overwriting the previous line's newline */ 
    str[i] = '\0'; 

    return str; 
} 

Эти линии выйдут назад, поэтому отмените их. Это достаточно просто.

void reverse(char *start) { 
    size_t len = strlen(start); 

    for(char *end = &start[len-1]; start < end; start++, end--) { 
     char tmp = start[0]; 
     start[0] = end[0]; 
     end[0] = tmp; 
    } 
} 

Собираем все вместе ...

fseek(fp, 0, SEEK_END); 

char line[1024]; 
while(fgets_backwards(line, 1024, fp) != NULL) { 
    reverse(line); 
    printf("%s", line); 
} 

Заметь, я был небрежен о проверке ошибок. Каждый звонок до fseek должен быть проверен.


Примечание: Я написал эту часть до О.П. уточнил, что они хотели. Ну, это все еще довольно круто.

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

char *reverse_by_word(char *string) { 
    size_t len = strlen(string); 

    /* Allocate enough space to store string, and a null */ 
    char *reversed = malloc(len * sizeof(char)); 

    /* Initialize reversed to be an empty string so strcat knows where to start */ 
    /* There's no need to initialize the rest of the string, 
    /* the garbage from malloc will be overwritten */ 
    reversed[0] = '\0'; 

    /* Read the string backwards, character by characer */ 
    for(int i = (int)len - 1; i >= 0; i--) { 
     /* If we see a space... */ 
     if(isspace(string[i])) { 
      /* Add the word after it to reversed */ 
      strcat(reversed, &string[i+1]); 
      /* Faithfully reproduce the whitespace after the word */ 
      strncat(reversed, &string[i], 1); 
      /* Chop the string off at the space */ 
      string[i] = '\0'; 
     } 
    } 

    return reversed; 
} 

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

С input и reversed такие же длины, используя strcpy без ограничений.

Мы можем проверить эти работы и точно воспроизводить все пробелы.

#include <assert.h> 

int main() { 
    char input[] = " Hello My\tname is "; 

    char *reversed = reverse_by_word(input); 
    printf("'%s'\n", reversed); 
    assert(strcmp(reversed, " is name\tMy Hello ") == 0); 
} 

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

char *reverse_by_word(const char *string) { 
    size_t len = strlen(string); 

    char *reversed = malloc(len * sizeof(char)); 
    reversed[0] = '\0'; 

    /* Read the string backwards, character by characer */ 
    int last_idx = (int)len; 
    for(int i = (int)len - 1; i >= 0; i--) { 
     /* If we see a space... */ 
     if(isspace(string[i])) { 
      /* Add the word before it, stop at the last word we saw. */ 
      strncat(reversed, &string[i+1], last_idx - i - 1); 
      /* Faithfully reproduce the whitespace. */ 
      strncat(reversed, &string[i], 1); 

      /* Remember the last place we printed up to. */ 
      last_idx = i; 
     } 
    } 

    return reversed; 
} 
+1

Спасибо за ваш ответ, я очень этому благодарен, извините, я отредактировал свой первоначальный вопрос, я новичок в переполнении стека и не знаю, как правильно отформатировать мой вопрос: p – JsmileyJ

+0

@JsmileyJ Не беспокойтесь, это было интересное упражнение. Я отредактировал ответ с решением вашего уточненного вопроса. Это не самый эффективный, но он работает. – Schwern

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

  • Нет связанных вопросов^_^