我试图将数据从FILE指针和字符串中检索出来。确定字符串缓冲区大小的最佳方法是什么?
char string[WHAT_SIZE?];
FILE *fp;
fp = fopen("info.dat", "r");
fgets(string, sizeof string, fp);
我是否将缓冲区大小设置为我认为适合该特定文件的大小?或者,如果不使用具有非可变缓冲区大小的字符串,是否有更有效的方法来执行此操作?
答案 0 :(得分:2)
一般来说,你只需选择一个尺码并随身携带。基于最大预期行长度或记录长度的选择或类似于输入类型的选择。只需确保检查返回代码并在行超出预期时处理案例。
你可以用一些技巧来获得一个确切的大小,但是我记不起在实践中不得不使用这些技巧:
做一个ftell,通过char读取char,计算直到你到达换行符,然后分配足够的内存,fseek倒带,并读取整行。
在文件末尾执行fseek以查找大小,然后将整个内容倒回并一次读入单个缓冲区。
答案 1 :(得分:1)
简单直接的方法是使用fseek()
和ftell()
。检索文件大小后,为数据分配缓冲区并使用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;
}
答案 2 :(得分:0)
一种可能性是根据需要动态分配缓冲区然后增长它(例如,使用realloc)。这可能需要为fgets编写一个包装函数,以检查它是否读取整行(换行符存储在缓冲区中)。它还必须处理EOF条件。
这可能不言而喻,但使用C来读取和解析具有可变宽度数据的文本文件是相当多的工作。它可能对你的情况没有意义甚至是可能的,但是如果你可以使用像Ruby,Python,Perl,Awk等那样的东西,你可以在很短的时间内完成任务。您可以使用那些可能需要100行C的工具完成几行代码。它们非常适合读取和解析分隔的文本文件。例如,下面的红宝石块逐行读取文本文件并用垂直条分开:
File.open("myfile.txt") { |file|
while ( line = file.gets )
puts "line: #{line}"
a = line.split( /\|/ )
puts "array: #{a}"
end
}
只是为了好玩,这是一个可能的实现,有几个待处理的TBD(错误检查)。主要问题(除了我没有看到的微妙错误)将解决如果你没有完全读取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);
}
答案 3 :(得分:0)
如果你真的打算从文件中读取行(这是使用fgets
而不是fread
)的常用原因,那么你需要什么是缓冲区足够长以容纳一条线。您通常无法提前知道,因此使用malloc
(或new
动态分配它,如果您使用的是C ++,但在这种情况下,您可能会更好地使用C ++的I / O工具)当你跑过一条太长的线时放大它。像这样:
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;
}
/* ... */
}
(警告:完全未经测试的代码;可能完全由错误和有毒废物组成。当然没有真正代码应该具有的错误条件的所有测试。)
你可能会建议不要让缓冲区无限制地增长,如果有些白痴喂你一个长篇疯狂的文件;在某些时候放弃。您可能希望将上述行为封装到函数中,特别是如果您有多个代码执行相同的操作。在这种情况下,您可能还希望将其状态(缓冲区及其当前大小)封装到struct
中。 (或者,如果你正在使用C ++,一个类,其中缓冲区扩展读取的东西将是一个成员函数。但是,再次,如果你使用C ++那么你应该使用它已经提供的设施。)