C语言 使用 fgetc() 逐行读取 c 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4293475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reading c file line by line using fgetc()
提问by helpermethod
This is how I've done it but I'm not sure this is the preferred idiom:
这就是我所做的,但我不确定这是首选的习惯用法:
FILE *fp = fopen(argv[0], "r");
// handle fopen() returning NULL
while (!feof(fp)) {
char buffer[80]; // statically allocated, may replace this later with some more sophisticated approach
int num_chars = 0;
for (int ch = fgetc(fp); ch != EOF && ch != '\n'; ch = fgetc()) {
buffer[num_chars++] = ch;
}
// null-terminate the string
buffer[num_chars] = 'int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && c != '\n' && dst < end)
*dst++ = c;
*dst = 'int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && dst < end)
{
if ((*dst++ = c) == '\n')
break;
}
*dst = 'static int endofline(FILE *ifp, int c)
{
int eol = (c == '\r' || c == '\n');
if (c == '\r')
{
c = getc(ifp);
if (c != '\n' && c != EOF)
ungetc(c, ifp);
}
return(eol);
}
';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
';
printf("%s\n", buffer);
}
Is this okay, any suggestions to improve this?
这可以吗,有什么建议可以改进吗?
回答by Jonathan Leffler
If you are not going to use fgets()(perhaps because you want to remove the newline, or you want to deal with "\r", "\n"or "\r\n"line endings, or you want to know how many characters were read), you can use this as a skeleton function:
如果您不打算使用fgets()(可能是因为您想删除换行符,或者您想处理"\r","\n"或"\r\n"行尾,或者您想知道读取了多少个字符),您可以将其用作骨架函数:
int get_line(FILE *fp, char *buffer, size_t buflen)
{
char *end = buffer + buflen - 1; /* Allow space for null terminator */
char *dst = buffer;
int c;
while ((c = getc(fp)) != EOF && !endofline(fp, c) && dst < end)
*dst++ = c;
*dst = 'void copy_file(FILE *in, FILE *out)
{
char buffer[4096];
size_t nbytes;
while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), in)) != 0)
{
if (fwrite(buffer, sizeof(char), nbytes, out) != nbytes)
err_error("Failed to write %zu bytes\n", nbytes);
}
}
';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
It recognizes only newline as the end of line; it drops the newline. It does not overflow the buffer; it does not discard excess characters, so if called upon to read a very long line, it will read the line in chunks; it returns the number of characters read. If you need to distinguish between overflow and a line that happens to be the length of the buffer - 1, then you probably need to preserve the newline - with consequential changes in the code:
它仅将换行符识别为行尾;它丢弃换行符。它不会溢出缓冲区;它不会丢弃多余的字符,因此如果需要读取很长的行,它将分块读取该行;它返回读取的字符数。如果您需要区分溢出和恰好是缓冲区长度的行 - 1,那么您可能需要保留换行符 - 并在代码中进行相应的更改:
copy_file(fp, stdout);
There are endless minor variants on this, such as discarding any excess characters if the line has to be truncated. If you want to handle DOS, (old) Mac or Unix line endings, then borrow a leaf out of the CSV code from "The Practice of Programming"by Kernighan & Pike (an excellent book) and use:
对此有无数次要变体,例如如果必须截断该行,则丢弃任何多余的字符。如果您想处理 DOS、(旧)Mac 或 Unix 行尾,请从Kernighan & Pike 的“编程实践”(一本优秀的书)中借用 CSV 代码的叶子并使用:
fgets (buffer, BUFFER_SIZE, fp);
Then you can use that in place of the c != '\n'test:
然后你可以用它代替c != '\n'测试:
fgets(buffer, sizeof buffer, stdin);
fputs(buffer, stdout); /* buffer contains a '\n' */
The other alternative way of dealing with the whole process is using fread()and fwrite():
处理整个过程的另一种替代方法是使用fread()and fwrite():
buffer[0] = 0;
if (!fgets(buffer, sizeof buffer, stdin)) /* error or eof */;
num_chars = strlen(buffer);
if (num_chars && (buffer[num_chars - 1] == '\n')) buffer[--num_chars] = 0;
puts(buffer); /* add a '\n' to output */
In context, you'd open the file and check it for validity, then call:
在上下文中,您将打开文件并检查其有效性,然后调用:
FILE *fp = fopen(argv[0], "r");
size_t len=1;
char c, *buffer=calloc(1,1);
/* handle fopen() returning NULL*/
while( c=fgetc(fp),!feof(fp) )
if( c=='\n' )
{
puts(buffer);
len=1;
*buffer=0;
}
else
strncat(buffer=realloc(buffer,++len),&c,1); /* check for NULL needed */
puts(buffer);
free(buffer);
fclose(fp);
回答by Milan
If you need every char in order to inspect it or modify or whatever else then use fgets. For everything else, use fgets.
如果您需要每个字符来检查或修改或其他任何字符,请使用 fgets。对于其他一切,请使用 fgets。
#include<stdio.h>
void main()
{
FILE *fp;
char c;
int ch=0,w=0,l=0;
fp=fopen("c:\read.txt","w");
clrscr();
if(fp==NULL)
{
printf("\n\n\tDOES NOT EXIXST");
getch();
exit(0);
}
while(!feof(fp))
{
c=fgetc(fp);
ch++;
if(c==' ')
{
w++;
}
if(c=='\n')
{
l++;
w++;
}
}
printf("\n\n\tTOTAL CHAR = %d\n\n\tTOTAL WORDS = %d\n\n\tTOTAL LINES = %d",ch,w,l);
}
Note that fgets will read until a new line or EOF is reached (or the buffer is full of course). New line character "\n" is also appended to the string if read from the file. Null character is also appended.
请注意, fgets 将一直读取,直到到达新行或 EOF(当然,或者缓冲区已满)。如果从文件中读取,新行字符“\n”也会附加到字符串。还附加了空字符。
On success, the function returns the same str parameter.
If the End-of-File is encountered and no characters have been read, the contents of str remain unchanged and a null pointer is returned.
If an error occurs, a null pointer is returned.
Use either ferror or feof to check whether an error happened or the End-of-File was reached.
成功时,该函数返回相同的 str 参数。
如果遇到 End-of-File 并且没有读取任何字符,则 str 的内容保持不变并返回空指针。
如果发生错误,则返回空指针。
使用 ferror 或 feof 来检查是否发生了错误或是否到达文件尾。
回答by Steve Emmerson
You're risking buffer overflow if the user inputs 80 characters or more.
如果用户输入 80 个或更多字符,您将面临缓冲区溢出的风险。
I'm with ThiefMaster: you should use fgets(), instead. Read the input into a buffer that's larger than any legitimate input and then check that the last character is a newline.
我和 ThiefMaster 在一起:你应该使用fgets(), 代替。将输入读入一个比任何合法输入都大的缓冲区,然后检查最后一个字符是否为换行符。
回答by pmg
Unless you're hoping to get a ultra-high efficient way to set the number of characters read, use fgets().
除非您希望获得一种超高效的方式来设置读取的字符数,否则请使用fgets().
Replacing your example with a similar but different simple fgets(), you "lose" the num_charsvariable.
用类似但不同的 simple 替换您的示例fgets(),您“丢失”了num_chars变量。
If you need to remove the last '\n'
如果您需要删除最后一个 '\n'
##代码##If the strings are really humongous (like 42 Mega bytes worth), you may be better off reading character by character and keeping count with num_charsthan using fgetsfirst and strlenlater.
如果字符串真的很庞大(例如 42 兆字节),您最好逐个字符读取并保持计数,而num_chars不是fgets先使用再使用strlen。
回答by user411313
No linesize-limit und strictly C89 (your code is only C99) like:
没有 linesize-limit 和严格的 C89(你的代码只是 C99),比如:
##代码##
