2017-02-01 19 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

「私の名前はこんにちは」は、「こんにちは私の名前です」から後ろにはありません。あなたは '私のハローという名前ですか? – Schwern

+0

複数の行がこんにちは、私の名前は、私の名前は、私の欲しいと言っています私の名前が最初に印刷され、次の行がこんにちは、ほぼ同じように置き換えられました – JsmileyJ

答えて

0

どのようにして文字を読み、後方に向かっているのかは、非効率的です。

PerlモジュールFile::Readbackwardsは読みやすいです。 PerlのIOはCに非常に近く、コードはよくコメントされています。

基本的なアルゴリズムは、ブロックを読み込んでバッファリングし、そのブロック内の行を見つけることですが、ファイルの最後から後方に移動します。

  1. ファイルを開きます。
  2. 最後に検索してください。
  3. 前のブロックに戻る。
  4. ブロックをバッファに読み込みます。

バッファーを取得したら、改行が見つかるまでバッファーを逆方向にスキャンします。今すぐあなたは完全なラインを持っています。改行が見つからない場合は、前のブロックを読み、もう一度やり直してください。


これは自明なことではありません。改行を見るまで、一度に1文字ずつ後方に読む方法は次のとおりです。私はfgetsインターフェイスを使って一度に1行を取得しました。

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はヌルバイトで細かくなります。これを非破壊的に行うことは可能ですが、後で編集することがあります。

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

関連する問題