【问题标题】:Print contents of a file in reverse line order以相反的行顺序打印文件的内容
【发布时间】:2017-02-01 01:46:46
【问题描述】:

所以我一直在尝试让我的程序正确打印输出时遇到问题。我有一个包含一些内容的文本文件,想从下往上打印。

    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

【问题讨论】:

  • 多行时你想要什么?
  • My name is Hello 不是从 Hello My name is 倒退。你的意思是is name My Hello
  • 说多行是 Hello 和 My name is,我想先打印我的名字,下一行是 Hello,就像他们换了地方一样

标签: c file output reverse


【解决方案1】:

你是怎么做的,阅读一个字符并向后寻找一个,效率很低。

Perl 模块File::Readbackwards 是一本好书。 Perl的IO与C非常接近,代码注释很好。

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

  1. 打开文件。
  2. 力争到底。​​li>
  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;
}

【讨论】:

  • 感谢您的回答,非常感谢,对不起,我编辑了最初的问题,我是堆栈溢出的新手,不知道如何正确格式化我的问题:p
  • @JsmileyJ 不用担心,这是一个有趣的练习。我已经用您澄清的问题的解决方案编辑了答案。这不是最有效的,但它确实有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-23
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多