以反向顺序打印文件的内容

时间:2017-02-01 01:46:46

标签: c file output reverse

所以我一直在试图让我的程序正确打印输出。我有一个包含一些内容的文本文件,并希望从下往上打印。

    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

1 个答案:

答案 0 :(得分:0)

你是如何做到这一点,阅读一个角色并向后寻找一个角色,效率低下。

Perl模块File::Readbackwards读得很好。 Perl的IO非常接近C,代码评论很好。

基本算法是读取和缓冲块,并在该块中查找行,但从文件末尾开始并向后移动。

  1. 打开文件。
  2. 寻求结束。
  3. 向后搜索上一个区块。
  4. 将该块读入缓冲区。
  5. 一旦有了缓冲区,就可以向后扫描缓冲区,直到找到换行符。现在你有一个完整的路线。如果找不到换行符,请阅读上一个块并重试。

    这是非常重要的,所以这里是你如何一次向后读一个字符,直到你看到换行符。我已经使用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;
    }