我就是这样做的,但我不确定这是首选的习语:
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] = '\0';
printf("%s\n", buffer);
}
这没关系,有什么建议可以改善吗?
答案 0 :(得分:13)
如果您不打算使用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 && c != '\n' && dst < end)
*dst++ = c;
*dst = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
它只识别换行符作为行尾;它放弃了换行符。它不会溢出缓冲区;它不会丢弃多余的字符,所以如果要求读取很长的行,它将以块的形式读取行;它返回读取的字符数。如果你需要区分溢出和恰好是缓冲区长度的行 - 1,那么你可能需要保留换行符 - 代码中的相应变化:
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 = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
此处有无穷无尽的小变体,例如,如果必须截断该行,则丢弃任何多余的字符。如果你想处理DOS,(旧的)Mac或Unix行结尾,那么借用Kernighan&amp ;;的"The Practice of Programming"中的CSV代码。派克(一本优秀的书)并使用:
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);
}
然后您可以使用它代替c != '\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 = '\0';
return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}
处理整个过程的另一种方法是使用fread()
和fwrite()
:
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);
}
}
在上下文中,您打开文件并检查其有效性,然后调用:
copy_file(fp, stdout);
答案 1 :(得分:1)
如果您需要每个字符来检查或修改或其他任何其他使用fgets。 对于其他一切,请使用fgets。
fgets (buffer, BUFFER_SIZE, fp);
请注意,fgets将会读取,直到达到新行或EOF(或者缓冲区已满)。如果从文件中读取,则新行字符“\ n”也会附加到字符串中。还附加空字符。
成功时,该函数返回相同的str参数 如果遇到文件结尾且未读取任何字符,则str的内容保持不变,并返回空指针。
如果发生错误,则返回空指针 使用ferror或feof来检查是否发生了错误或是否已达到文件结尾。
答案 2 :(得分:1)
如果用户输入80个字符或更多字符,则存在缓冲区溢出的风险。
我和ThiefMaster在一起:你应该使用fgets()
。将输入读入比任何合法输入更大的缓冲区,然后检查最后一个字符是换行符。
答案 3 :(得分:1)
除非您希望以超高效的方式设置读取的字符数,否则请使用fgets()
。
使用类似但不同的简单fgets()
替换您的示例,您将“丢失”num_chars
变量。
fgets(buffer, sizeof buffer, stdin);
fputs(buffer, stdout); /* buffer contains a '\n' */
如果您需要删除最后一个'\ n'
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 */
如果字符串真的非常庞大(比如42兆字节),那么你最好逐个字符地阅读并使用num_chars
计算,而不是先使用fgets
然后再使用strlen
答案 4 :(得分:-1)
没有lineize-limit和严格的C89(你的代码只有C99),如:
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);
答案 5 :(得分:-2)
#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);
}