在c中从文件流获取数据
我试图从文件指针中检索数据并将其转换成字符串。确定字符串缓冲区大小的最佳方法是什么在c中从文件流获取数据,c,buffer,filestream,C,Buffer,Filestream,我试图从文件指针中检索数据并将其转换成字符串。确定字符串缓冲区大小的最佳方法是什么 char string[WHAT_SIZE?]; FILE *fp; fp = fopen("info.dat", "r"); fgets(string, sizeof string, fp); 我是否将缓冲区大小设置为适合该特定文件的大小?或者有没有一种更有效的方法可以在不使用缓冲区大小不可变的字符串的情况下执行此操作?一种可能是动态分配缓冲区,然后根据需要对其进行增长(例如,使用realloc)。这可能需
char string[WHAT_SIZE?];
FILE *fp;
fp = fopen("info.dat", "r");
fgets(string, sizeof string, fp);
我是否将缓冲区大小设置为适合该特定文件的大小?或者有没有一种更有效的方法可以在不使用缓冲区大小不可变的字符串的情况下执行此操作?一种可能是动态分配缓冲区,然后根据需要对其进行增长(例如,使用realloc)。这可能需要为fgets编写一个包装函数,检查它是否读取了整行(换行符存储在缓冲区中)。它还必须处理EOF条件 这可能不言而喻,但是使用C来读取和解析具有可变宽度数据的文本文件是相当多的工作。这对于您的情况可能没有意义,甚至是不可能的,但是如果您可以使用Ruby、Python、Perl、Awk等工具,您可能可以在很短的时间内完成任务。使用这些工具,您可以在几行代码中完成可能需要100行C语言的工作。它们非常适合读取和解析分隔文本文件。例如,以下ruby块逐行读取文本文件,并通过垂直条将其拆分:
File.open("myfile.txt") { |file|
while ( line = file.gets )
puts "line: #{line}"
a = line.split( /\|/ )
puts "array: #{a}"
end
}
为了好玩,这里有一个可能的实现,需要处理几个TBD(错误检查)。主要的问题(除了我没有看到的细微的bug)是,如果您没有完全读取EOF,那么要解决缓冲区的释放问题
int myReadLine // return non-zero if line returned, 0 on eof (see tbd below)
(
FILE *fp, // (I) open file handle for reading
char **buf, // (IO) buffer allocated by this function. It is freed by
// this function when EOF is hit. TBD: Should write a myFreeLine
// (for encapsulation purposes) to free this buffer for cases where
// you quit calling
int *len // (IO) current length of buffer pointed to by buf
)
{
char *ret;
char *pos;
int curlen;
int remaining;
if ( *len == 0 )
{
assert( *buf == NULL );
// pick a number out of the air. Could be app-specific. In debug
// it may be nice to start very small to force reallocs to exercise all
// code paths.
*len = 2;
// tbd: need error checking
*buf = (char*)malloc( *len * sizeof( char ));
}
pos = *buf;
remaining = *len;
while ( 1 )
{
ret = fgets( pos, remaining, fp );
if ( ret == NULL )
{
// tbd: should check if error occurred here. For now assuming eof
free( *buf );
*buf = NULL;
*len = 0;
return 0;
}
// check to see if we got the entire line.
curlen = strlen( *buf );
if ( (*buf)[curlen - 1] == '\n' ) // tbd: check for \r?
{
// apparently we got the whole line
// remove the end of line (at least that's what I would want)
(*buf)[curlen - 1] = '\0';
return 1;
}
else
{
// failed to get entire line
assert( curlen + 1 == *len );
// grow the buffer (tbd: realloc is a pain ... need error checking)
*len *= 2; // doubling is often a good plan
*buf = (char*)realloc( *buf, *len );
// set the "amount left" variables correctly for next iteration
remaining = *len - curlen;
pos = *buf + curlen;
}
} // while forever
// don't expect to get here
assert( 0 );
}
下面是一个示例调用:
void readfile(char *filepath)
{
int len = 0;
char *buf = NULL;
FILE *fp=fopen(filepath,"rt");
while ( myReadLine( fp, &buf, &len ))
printf( "'%s'\n", buf );
fclose(fp);
}
一般来说,你只需要选择一个尺码,然后就可以了。根据输入类型的最大预期行长度或记录长度或类似内容进行选择。只要确保检查返回代码,并在线路长度超过预期时处理该情况 你可以使用一些技巧来获得准确的尺寸,但我不记得在实践中必须使用这些技巧:
如果您实际上打算从文件中读取行(这是使用
fgets
而不是,比如说,fread
)的通常原因),那么您需要的是缓冲区足够长以容纳行。你经常无法事先知道,所以用<代码> MalOC/size_t line_size = 256; /* reasonable initial default */
char * line_buffer = malloc(line_size);
line_buffer[line_size-2] = '\n'; /* yes, 2 */
/* You should check for malloc failure here */
while (whatever) {
/* ... */
fgets(line_buffer, line_size, fp); /* should check for failure and EOF here too */
while (line_buffer[line_size-2] != '\n') {
/* we filled the buffer, and the last character wasn't a newline */
size_t new_line_size = 2*line_size;
line_buffer = realloc(line_buffer, new_line_size); /* should check for failure here */
line_buffer[new_line_size-2] = '\n';
fgets(line_buffer+line_size-1, new_line_size-line_size+1, fp); /* should check for failure and EOF */
line_size = new_line_size;
}
/* ... */
}
(警告:完全未经测试的代码;可能完全由bug和有毒废物组成。当然没有真正代码应该具备的所有错误条件测试。)
如果某个白痴给你提供了一个长得离谱的文件,你最好不要让缓冲区无限增长;在某个时候放弃。您可能希望将上述行为封装到一个函数中,尤其是当您有多个代码位执行相同的操作时。在这种情况下,您可能还希望将其状态(缓冲区及其当前大小)封装到
结构中。(或者,如果你使用C++,一个类,它扩展了读取的东西将是一个成员函数。但是,如果你使用C++,那么你应该使用它已经提供的工具。)< /P> < P>简单易懂的方式是使用<代码> fsikk()/<代码>和 ftele>>(<)/Cudio>。检索文件大小后,为数据分配缓冲区,并使用fread()
读取文件
此示例是检索文件确切大小的常用方法
#include <stdio.h>
#include <stdlib.h>
/* excepts file stream which is already opened */
long get_filesize(FILE *fp)
{
long filesize;
if( fseek(fp, 0, SEEK_END) ) != 0)
exit(EXIT_FAILURE); /* exit with errorcode if fseek() fails */
filesize = ftell(fp);
rewind(fp);
return filesize;
}
int main(void)
{
FILE *fp;
long filesize;
unsigned char *buffer;
fp = fopen("info.dat", "rb");
filesize = get_filesize(fp);
if(filesize < 1) exit(EXIT_FAILURE);
buffer = malloc( filesize * sizeof(unsigned char) );
if(buffer == NULL) exit(EXIT_FAILURE);
/* checking the fread return value is not necessary but recommended */
if((fread(buffer, sizeof(unsigned char), filesize, fp)) != filesize)
exit(EXIT_FAILURE);
fclose(fp);
/* ===== use the file here ===== */
free(buffer); /* remember to free the memory */
return EXIT_SUCCESS;
}
#包括
#包括
/*已打开的文件流除外*/
长获取文件大小(文件*fp)
{
长文件大小;
如果(fseek(fp,0,SEEK_END))!=0)
exit(exit_FAILURE);/*如果fseek()失败,则使用错误代码退出*/
filesize=ftell(fp);
倒带(fp);
返回文件大小;
}
内部主(空)
{
文件*fp;
长文件大小;
无符号字符*缓冲区;
fp=fopen(“信息数据”、“rb”);
filesize=get_filesize(fp);
如果(文件大小<1)退出(退出失败);
buffer=malloc(filesize*sizeof(unsigned char));
如果(缓冲区==NULL)退出(退出失败);
/*不需要检查fread返回值,但建议检查fread返回值*/
if((fread(buffer,sizeof(unsigned char),filesize,fp))!=filesize)
退出(退出失败);
fclose(fp);
/*====在此处使用文件=====*/
释放(缓冲区);/*记住释放内存*/
返回退出成功;
}
谢谢您的回答,但是fread用于二进制文件,而我的文件是一个带有“|”分隔符的简单ASCII文件。您可以在文本文件上使用fread()
。它只需读取请求的字节数(大小*元素)。Paynter在处理文件时应始终使用二进制模式。“文本”模式是一种令人憎恶的模式,它只会导致更多的问题而不是解决问题。在我看来,这似乎是一个糟糕的建议。当您可以适应文件的实际内容时,为什么要“选择一个大小并随它去”呢?我认为最好是临时进行自适应,而不是像这里描述的那样进行回溯,这样(例如)你就可以获取不可查找文件的输入。这取决于你在做什么。大量的实际工作程序都有一个固定的输入缓冲区。输入到系统中后,他们可以将其复制到适当大小的分配字符串中,甚至可以动态解析它并用下一行覆盖它。只要你对那些不合适的线路有一个好的策略,这是完全合理的。我建议,对那些不合适的线路有一个好的策略可能比在每一个不足以证明其合理性的情况下安排一个合适的线路要困难得多