以逆行顺序打印文件内容
Print contents of a file in reverse line order
所以我一直 运行 遇到问题试图让我的程序正确打印输出。我有一个包含一些内容的文本文件,我想从下往上打印它。
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
你是怎么做的,读取一个字符然后向后查找一个字符,效率很低。
Perl 模块 File::Readbackwards 是一本好书。 Perl的IO和C很接近,代码注释也很好
基本算法是读取和缓冲块,并在该块内查找行,但从文件末尾开始并向后移动。
- 打开文件。
- 求到最后。
- 向后查找上一个块。
- 将该块读入缓冲区。
获得该缓冲区后,向后扫描缓冲区,直到找到换行符。现在你有了一条完整的生产线。如果找不到换行符,请阅读上一个块并重试。
那是 non-trivial,所以这是您一次向后读取一个字符直到看到换行符的方法。我使用 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] = '[=10=]';
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] = '[=13=]';
/* 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] = '[=13=]';
}
}
return reversed;
}
这是破坏性版本,string
被空字节切碎。这样做是可行的 non-destructively,稍后我可能会对其进行编辑。
由于 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 );
}
这是 non-destructive 版本。基本相同的想法,但不是用空字节标记我们已经打印的地方,而是在 last_idx
.
中记住它
char *reverse_by_word( const char *string ) {
size_t len = strlen(string);
char *reversed = malloc( len * sizeof(char) );
reversed[0] = '[=15=]';
/* 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;
}
所以我一直 运行 遇到问题试图让我的程序正确打印输出。我有一个包含一些内容的文本文件,我想从下往上打印它。
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
你是怎么做的,读取一个字符然后向后查找一个字符,效率很低。
Perl 模块 File::Readbackwards 是一本好书。 Perl的IO和C很接近,代码注释也很好
基本算法是读取和缓冲块,并在该块内查找行,但从文件末尾开始并向后移动。
- 打开文件。
- 求到最后。
- 向后查找上一个块。
- 将该块读入缓冲区。
获得该缓冲区后,向后扫描缓冲区,直到找到换行符。现在你有了一条完整的生产线。如果找不到换行符,请阅读上一个块并重试。
那是 non-trivial,所以这是您一次向后读取一个字符直到看到换行符的方法。我使用 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] = '[=10=]';
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] = '[=13=]';
/* 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] = '[=13=]';
}
}
return reversed;
}
这是破坏性版本,string
被空字节切碎。这样做是可行的 non-destructively,稍后我可能会对其进行编辑。
由于 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 );
}
这是 non-destructive 版本。基本相同的想法,但不是用空字节标记我们已经打印的地方,而是在 last_idx
.
char *reverse_by_word( const char *string ) {
size_t len = strlen(string);
char *reversed = malloc( len * sizeof(char) );
reversed[0] = '[=15=]';
/* 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;
}