在C中逐字符读取文件
Posted
技术标签:
【中文标题】在C中逐字符读取文件【英文标题】:Reading a file character by character in C 【发布时间】:2011-06-16 22:16:51 【问题描述】:我正在用 C 编写一个 BF 解释器,但在读取文件时遇到了问题。我曾经使用scanf
来读取第一个字符串,但是你的BF 代码中不能有空格或cmets。
现在这就是我所拥有的。
char *readFile(char *fileName)
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
*code++ = (char)fgetc(file);
while(*code != EOF);
return code;
我知道问题出在我如何将文件中的下一个字符分配给代码指针,但我只是不确定那是什么。 我缺乏指针知识,这是本练习的重点。 解释器工作正常,全部使用指针,我只是在读取文件时遇到问题。
(稍后我将实现仅将+-><[].,
读取到文件中,尽管如果有人有好的方法可以做到这一点,如果你能告诉我会很棒!)
【问题讨论】:
【参考方案1】:您的代码有很多问题:
char *readFile(char *fileName)
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
*code++ = (char)fgetc(file);
while(*code != EOF);
return code;
-
如果文件大于 1000 字节怎么办?
每次读取字符时都会增加
code
,并且将code
返回给调用者(即使它不再指向内存块的第一个字节,因为它是由malloc
返回的)。
您将fgetc(file)
的结果转换为char
。在将结果转换为 char
之前,您需要检查 EOF
。
维护malloc
返回的原始指针很重要,以便以后可以释放它。如果我们忽略文件大小,我们仍然可以通过以下方式实现:
char *readFile(char *fileName)
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1000);
while ((c = fgetc(file)) != EOF)
code[n++] = (char) c;
// don't forget to terminate with the null character
code[n] = '\0';
return code;
有多种系统调用可以为您提供文件的大小;一个常见的是stat
。
【讨论】:
什么是计算文件中字符数的最简单方法,以便我可以将“1000”设置为?另外,我不确定您所说的数字 2 是什么意思,我知道我这样做是错误的,但是我将如何修改呢? @pwnmonkey:我的意思是当它指向文件的 end 时,您正在返回code
,而不是在它指向开头时。
@deamlax 您的示例似乎有一个小错字。 fgets
需要多个参数。你的意思是fgetc
或许?
我不得不将 malloc 转换为 code = (char*)malloc(1000);
,因为在 c++ 中将 char* 转换为 void 时出错
@A.k.如果您使用 C++,请不要使用 malloc
。使用std::istream
读取文件,并使用std::vector<char>
或其他结构来处理缓冲区。使用new char[]
作为最后的手段,但在编写 C++ 代码时避免使用malloc
,因为有更好的(类型安全)替代方案:)【参考方案2】:
从@dreamlax 扩展上述代码
char *readFile(char *fileName)
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
while ((c = fgetc(file)) != EOF)
code[n++] = (char)c;
code[n] = '\0';
return code;
这会给你文件的长度,然后逐个字符地读取它。
【讨论】:
你能解释一下 fseek 和 ftell 是如何工作的吗?我认为您需要某种循环来计算文件的字符数。 将fseek
视为重新定位光标的一种方式。 fseek(文件,0,SEEK_END);将光标放在文件末尾,然后ftell
告诉您光标在哪里。这给了你文件的大小。 fseek(file, 0, SEEK_SET);
将光标放回文件的开头以便可以读取。如果不这样做,则会从头开始读取文件,这会导致错误并破坏整个操作。【参考方案3】:
这是一种简单的方法,可以忽略除有效的脑残字符之外的所有内容:
#define BF_VALID "+-><[].,"
if (strchr(BF_VALID, c))
code[n++] = c;
【讨论】:
是的,我以前使用过这个,但问题是如果文件中有换行符,它会将这些换行符放入字符串中。如果我想在一行中将纯 BF 代码写入文件,甚至将其打印到控制台,这很糟糕。 @pwnmonkey:不,这不会将任何换行符存储到目标字符串中(如果您将\n
添加到BF_VALID
,它会)。【参考方案4】:
每次调用函数时,文件都被打开而不是关闭
【讨论】:
是的,我知道,我已经更改了,但感谢您告诉我。【参考方案5】:我认为最重要的问题是你在读入内容时递增code
,然后返回code
的最终值,即你将返回一个指向end 的字符串。您可能想在循环之前复制code
,然后将其返回。
此外,C 字符串需要以空值结尾。您需要确保将'\0'
直接放在您读入的最后一个字符之后。
注意:您可以只使用fgets()
一次性获得整行。
【讨论】:
对,但我将排除任何不是 BF 代码的内容,因此一次运行更容易。【参考方案6】:两者中的任何一个都可以解决问题 -
char *readFile(char *fileName)
FILE *file;
char *code = malloc(1000 * sizeof(char));
char *p = code;
file = fopen(fileName, "r");
do
*p++ = (char)fgetc(file);
while(*p != EOF);
*p = '\0';
return code;
char *readFile(char *fileName)
FILE *file;
int i = 0;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
code[i++] = (char)fgetc(file);
while(code[i-1] != EOF);
code[i] = '\0'
return code;
就像其他发帖人指出的那样,您需要确保文件大小不超过 1000 个字符。另外,请记住在使用完毕后释放内存。
【讨论】:
【参考方案7】:这里的问题是双重的
a) 在检查读入的值之前增加指针,并且 b) 你忽略了fgetc()
返回一个 int 而不是 char 的事实。
第一个很容易解决:
char *orig = code; // the beginning of the array
// ...
do
*code = fgetc(file);
while(*code++ != EOF);
*code = '\0'; // nul-terminate the string
return orig; // don't return a pointer to the end
第二个问题更微妙 -fgetc
返回一个 int 以便可以将 EOF
值与任何可能的 char 值区分开来。解决此问题时使用临时 int 进行 EOF
检查,并且可能使用常规 while 循环而不是 do / while。
【讨论】:
如果EOF
介于 -1 和 -128 之间,那么 是 可表示为 char
值(假设实现的普通 char
类型已签名) .在这种情况下,从int
到char
的转换完全定义为当值可以由char
表示时,EOF
可能就是这种情况。以上是关于在C中逐字符读取文件的主要内容,如果未能解决你的问题,请参考以下文章
使用 mmap 在 C 中逐行读取文件的最佳方法? [关闭]
Objective C - 创建文本文件以在 Cocoa 中逐行读取和写入