文件如下所示:
abcd
efgh
ijkl
我想使用C
读取文件,以便它首先读取最后一行:
ijkl
efgh
abcd
我似乎无法找到不使用array
存储的解决方案。请帮忙。
edit0: 感谢所有的答案。只是为了让你知道,我是创建这个文件的人。那么,我可以以相反的顺序创建它吗?这可能吗?
答案 0 :(得分:9)
它是这样的:
fseek
在文件结尾前寻找一个字节。不能保证最后一行会有EOL,所以最后一个字节并不重要。fgetc
读取一个字节。fseek
后退两个字节并使用fgetc
检查该字节。基本上你一直在做(4)和(5),同时跟踪你找到一条线的开头时的位置,这样你就可以在开始扫描下一行之前找回那里。 / p>
只要您在文本模式下打开文件,就不必担心Windows上的多字节EOL(感谢提示Lutz先生)。
如果您碰巧获得了不可搜索的输入(例如管道),那么除非您想先将输入转储到临时文件中,否则您运气不佳。
所以你可以做到,但它相当难看。
如果你有mmap
可用,并且你正在使用的“文件”是可映射的,你可以使用mmap
和指针做同样的事情。这个技术几乎是一样的:从最后开始然后向后找到上一行的结尾。
回复:“我是创建此文件的人。那么,我可以以相反的顺序创建它吗?这可能吗?”
你会遇到同样的问题,但情况会更糟。 C中的文件本质上是连续的字节列表,从开头开始到结尾;你试图反对这个基本的财产,反对基本面永远不会有趣。
您真的需要纯文本文件中的数据吗?也许您需要text / plain作为最终输出但是一直都是如此?您可以将数据存储在索引的二进制文件(甚至可能是SQLite数据库)中,然后您只需担心将索引保留(或窗口化)在内存中并且这不太可能是一个问题(如果是,请使用一个“真正的”数据库);然后,当你拥有所有的线条时,只需反转索引即可离开。
答案 1 :(得分:3)
在伪代码中:
open input file
while (fgets () != NULL)
{
push line to stack
}
open output file
while (stack no empty)
{
pop stack
write popped line to file
}
以上是有效的,没有搜索(慢速操作)并且文件被顺序读取。然而,上面有两个陷阱。
第一个是fgets
电话。提供给fgets
的缓冲区可能不足以容纳输入的整行,在这种情况下,您可以执行以下操作之一:再次读取并连接;推送部分行并向后半部分添加逻辑以修复部分行或将行包装到链接列表中,并仅在遇到换行符/ eof时推送链接列表。
当文件大于可用ram来保存堆栈时,会发生第二个陷阱,在这种情况下,只要达到某个阈值内存使用量,就需要将堆栈结构写入临时文件。
答案 2 :(得分:1)
以下代码应该进行必要的反转:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fd;
char len[400];
int i;
char *filename = argv[1];
int ch;
int count;
fd = fopen(filename, "r");
fseek(fd, 0, SEEK_END);
while (ftell(fd) > 1 ){
fseek(fd, -2, SEEK_CUR);
if(ftell(fd) <= 2)
break;
ch =fgetc(fd);
count = 0;
while(ch != '\n'){
len[count++] = ch;
if(ftell(fd) < 2)
break;
fseek(fd, -2, SEEK_CUR);
ch =fgetc(fd);
}
for (i =count -1 ; i >= 0 && count > 0 ; i--)
printf("%c", len[i]);
printf("\n");
}
fclose(fd);
}
答案 3 :(得分:0)
以下适用于Linux,其中文本文件行分隔符为“\ n”。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readfileinreverse(FILE *fp)
{
int i, size, start, loop, counter;
char *buffer;
char line[256];
start = 0;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
buffer = malloc((size+1) * sizeof(char));
for (i=0; i< size; i++)
{
fseek(fp, size-1-i, SEEK_SET);
buffer[i] = fgetc(fp);
if(buffer[i] == 10)
{
if(i != 0)
{
counter = 0;
for(loop = i; loop > start; loop--)
{
if((counter == 0) && (buffer[loop] == 10))
{
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
start = i;
printf("%s\n",line);
}
}
}
if(i > start)
{
counter = 0;
for(loop = i; loop > start; loop--)
{
if((counter == 0) && ((buffer[loop] == 10) || (buffer[loop] == 0)))
{
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
printf("%s\n",line);
return;
}
}
int main()
{
FILE *fp = fopen("./1.txt","r");
readfileinreverse(fp);
return 0;
}
答案 4 :(得分:0)
也许,这就是诀窍,它整体反转了文件的内容 就像一个字符串
您可以稍后显示输出,甚至可以将其写入文件。代码如下:
#include <stdio.h>
#include <String.h>
int main(){
FILE *file;
char all[1000];
// give any name to read in reverse order
file = fopen("anyFile.txt","r");
// gets all the content and stores in variable all
fscanf(file,"%[]",all);
// Content of the file
printf("Content Of the file %s",all);
// reverse the string
printf("%s",strrev(all));
fclose(file);
return 0;
}
答案 5 :(得分:0)
我知道这个问题已被 awnsered,但接受的 awnser 不包含代码片段,其他片段感觉太复杂了。 这是我的实现:
#include <stdio.h>
long file_size(FILE* f) {
fseek(f, 0, SEEK_END); // seek to end of file
long size = ftell(f); // get current file pointer
fseek(f, 0, SEEK_SET); // seek back to beginning of file
return size;
}
int main(int argc, char* argv[]) {
FILE *in_file = fopen(argv[1], "r");
long in_file_size = file_size(in_file);
printf("Got file size: %ld\n", in_file_size);
// Start from end of file
fseek(in_file, -1, SEEK_END); // seek to end of file
for (int i = in_file_size; i > 0; i--) {
char current_char = fgetc(in_file); // This progresses the seek location
printf("Got char: |%c| with hex: |%x|\n", current_char, current_char);
fseek(in_file, -2, SEEK_CUR); // Go back 2 bytes (1 to compensate)
}
printf("Done\n");
fclose(in_file);
}