2017-02-01 53 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); 

從這個A輸出樣本將

輸入:

Hello 
My name is 

輸出:

si eman yM 
olleH 

我想要什麼輸出爲:

My name is 
Hello 
+0

當多於一行時,你想要什麼? – Schwern

+0

'我的名字是你好'不是從'你好我的名字是'倒退。你的意思是'是我的你好嗎? – Schwern

+0

說多行是你好,我的名字是,我想我的名字是先打印,下一行是你好,幾乎就像他們轉換的地方 – 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


注意我寫這部分之前的OP澄清他們想要的東西。哦,那還是很酷的。

既然你有一個完整的行,你可以解決它與閱讀文件分開。

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被空字節切碎。可以非破壞性地做到這一點,我可能會稍後再編輯它。

由於inputreversed是相同的長度,使用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