从C文件中逐个字符读取
character by character reading from a file in C
如何将文件中的文本读入动态字符数组?
我找到了一种计算文件中字符数并创建动态数组的方法,但我不知道如何将字符分配给数组的元素?
FILE *text;
char* Str;
int count = 0;
char c;
text = fopen("text.txt", "r");
while(c = (fgetc(text))!= EOF)
{
count ++;
}
Str = (char*)malloc(count * sizeof(char));
fclose(text);
要按照您的要求进行操作,您必须再次阅读整个文件:
...
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
// do stuff with it
// ...
fclose(text);
但是你的字符串不是以这种方式终止的。如果您尝试使用一些常见的字符串函数,例如 strlen
.
,这会给您带来一些麻烦
要正确地以 null 终止您的字符串,您必须为一个额外的字符分配 space 并将最后一个字符设置为 '\0':
...
// allocate count + 1 (for the null terminator)
Str = (char*)malloc((count + 1) * sizeof(char));
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
// add null terminator
Str[count] = '[=11=]';
// do stuff with it
// ...
fclose(text);
现在如果你想知道文件中的字符数而不用一个一个地计算它们,你可以用更有效的方式得到这个数字:
...
text = fopen("text.txt", "r");
// seek to the end of the file
fseek(text, 0L, SEEK_END);
// get your current position in that file
count = ftell(text)
// allocate count + 1 (for the null terminator)
Str = (char*)malloc((count + 1) * sizeof(char));
...
现在以更结构化的形式进行介绍:
// open file
FILE *text = fopen("text.txt", "r");
// seek to the end of the file
fseek(text, 0L, SEEK_END);
// get your current position in that file
ssize_t count = ftell(text)
// allocate count + 1 (for the null terminator)
char* Str = (char*)malloc((count + 1) * sizeof(char));
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
fclose(text);
// add null terminator
Str[count] = '[=13=]';
// do stuff with it
// ...
编辑:
正如 Andrew Henle 所指出的,并非每个 FILE
流都是可搜索的,您甚至不能依赖能够再次读取文件(或者文件在读取时具有相同的 length/content再次)。即使这是公认的答案,如果您事先不知道您正在处理的是哪种文件流,他的解决方案绝对是可行的方法。
对于二进制文件,可以使用fseek
和ftell
不读取文件就知道大小,分配内存然后读取所有内容:
...
text = fopen("text.txt", "r");
fseek(txt, 0, SEEK_END);
char *ix = Str = malloc(ftell(txt);
while(c = (fgetc(text))!= EOF)
{
ix++ = c;
}
count = ix - Str; // get the exact count...
...
对于文本文件,在具有多字节行尾的系统上(如使用 \r\n
的 Windows),这将分配比所需更多的字节。你当然可以扫描文件两次,第一次扫描大小,第二次实际读取字符,但你也可以忽略额外的字节,或者你可以 realloc
:
...
count = ix - Str;
Str = realloc(Str, count);
...
当然对于真实世界的程序,你应该控制所有io和分配函数的return值:fopen
、fseek
、fteel
、malloc
和 realloc
...
C 中没有可移植的、符合标准的方法来预先知道可以从 FILE
流中读取多少字节。
首先,流可能甚至不可搜索 - 它可以是管道或终端甚至是套接字连接。在这样的流上,一旦你读取了输入,它就消失了,再也不会被读取了。您可以推回一个 char
值,但这不足以知道还有多少数据有待读取,或重新读取整个流。
即使流是指向一个您可以查找的文件,您也不能在可移植的、严格符合 C 代码中使用 fseek()
/ftell()
来知道文件有多大是。
如果它是二进制流,则不能使用 fseek()
查找文件末尾 - 这是明确未定义的行为 per the C standard:
... A binary stream need not meaningfully support fseek
calls with a whence value of SEEK_END
.
Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END)
, has undefined behavior for a binary stream ...
所以你不能在二进制流中便携地使用 fseek()
。
并且您不能使用 ftell()
来获取文本流的字节数。每 the C standard again:
For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
确实存在从 ftell()
返回的值与字节数完全不同的系统。
了解您可以从流中读取多少字节的唯一可移植、符合标准的方法是实际读取它们,您不能依赖能够再次读取它们。
如果要将整个流读入内存,则必须不断地重新分配内存,或者使用其他一些动态方案。
这是将流的全部内容读入内存的一种非常低效但可移植且严格符合标准的方法(为了算法清晰和防止垂直滚动条出现,省略了所有错误检查和头文件 - 它确实需要错误检查并需要正确的头文件):
// get input stream with `fopen()` or some other manner
FILE *input = ...
size_t count = 0;
char *data = NULL;
for ( ;; )
{
int c = fgetc( input );
if ( c == EOF )
{
break;
}
data = realloc( data, count + 1 );
data[ count ] = c;
count++;
}
// optional - terminate the data with a '[=10=]'
// to treat the data as a C-style string
data = realloc( data, count + 1 );
data[ count ] = '[=10=]';
count++;
无论流是什么,这都有效。
在 POSIX 风格的系统上,例如 Linux,您可以使用 fileno()
and fstat()
来获取文件的大小(同样,省略了所有错误检查和头文件) :
char *data = NULL;
FILE *input = ...
int fd = fileno( input );
struct stat sb;
fstat( fd, &sb );
if ( S_ISREG( sb.st_mode ) )
{
// sb.st_size + 1 for C-style string
char *data = malloc( sb.st_size + 1 );
data[ sb.st_size ] = '[=11=]';
}
// now if data is not NULL you can read into the buffer data points to
// if data is NULL, see above code to read char-by-char
// this tries to read the entire stream in one call to fread()
// there are a lot of other ways to do this
size_t totalRead = 0;
while ( totalRead < sb.st_size )
{
size_t bytesRead = fread( data + totalRead, 1, sb.st_size - totalRead, input );
totalRead += bytesRead;
}
以上内容也适用于 Windows。您也可能会得到 some compiler warnings or have to use _fileno()
, _fstat()
and struct _stat
。*
您可能还需要 define the S_ISREG()
macro on Windows:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
* 那是 _fileno()
、_fstat()
和 struct _stat
,没有超链接 underline-munge。
如何将文件中的文本读入动态字符数组? 我找到了一种计算文件中字符数并创建动态数组的方法,但我不知道如何将字符分配给数组的元素?
FILE *text;
char* Str;
int count = 0;
char c;
text = fopen("text.txt", "r");
while(c = (fgetc(text))!= EOF)
{
count ++;
}
Str = (char*)malloc(count * sizeof(char));
fclose(text);
要按照您的要求进行操作,您必须再次阅读整个文件:
...
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
// do stuff with it
// ...
fclose(text);
但是你的字符串不是以这种方式终止的。如果您尝试使用一些常见的字符串函数,例如 strlen
.
要正确地以 null 终止您的字符串,您必须为一个额外的字符分配 space 并将最后一个字符设置为 '\0':
...
// allocate count + 1 (for the null terminator)
Str = (char*)malloc((count + 1) * sizeof(char));
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
// add null terminator
Str[count] = '[=11=]';
// do stuff with it
// ...
fclose(text);
现在如果你想知道文件中的字符数而不用一个一个地计算它们,你可以用更有效的方式得到这个数字:
...
text = fopen("text.txt", "r");
// seek to the end of the file
fseek(text, 0L, SEEK_END);
// get your current position in that file
count = ftell(text)
// allocate count + 1 (for the null terminator)
Str = (char*)malloc((count + 1) * sizeof(char));
...
现在以更结构化的形式进行介绍:
// open file
FILE *text = fopen("text.txt", "r");
// seek to the end of the file
fseek(text, 0L, SEEK_END);
// get your current position in that file
ssize_t count = ftell(text)
// allocate count + 1 (for the null terminator)
char* Str = (char*)malloc((count + 1) * sizeof(char));
// go back to the beginning
fseek(text, 0L, SEEK_SET);
// read
ssize_t readsize = fread(Str, sizeof(char), count, text);
if(readsize != count) {
printf("woops - something bad happened\n");
}
fclose(text);
// add null terminator
Str[count] = '[=13=]';
// do stuff with it
// ...
编辑:
正如 Andrew Henle 所指出的,并非每个 FILE
流都是可搜索的,您甚至不能依赖能够再次读取文件(或者文件在读取时具有相同的 length/content再次)。即使这是公认的答案,如果您事先不知道您正在处理的是哪种文件流,他的解决方案绝对是可行的方法。
对于二进制文件,可以使用fseek
和ftell
不读取文件就知道大小,分配内存然后读取所有内容:
...
text = fopen("text.txt", "r");
fseek(txt, 0, SEEK_END);
char *ix = Str = malloc(ftell(txt);
while(c = (fgetc(text))!= EOF)
{
ix++ = c;
}
count = ix - Str; // get the exact count...
...
对于文本文件,在具有多字节行尾的系统上(如使用 \r\n
的 Windows),这将分配比所需更多的字节。你当然可以扫描文件两次,第一次扫描大小,第二次实际读取字符,但你也可以忽略额外的字节,或者你可以 realloc
:
...
count = ix - Str;
Str = realloc(Str, count);
...
当然对于真实世界的程序,你应该控制所有io和分配函数的return值:fopen
、fseek
、fteel
、malloc
和 realloc
...
C 中没有可移植的、符合标准的方法来预先知道可以从 FILE
流中读取多少字节。
首先,流可能甚至不可搜索 - 它可以是管道或终端甚至是套接字连接。在这样的流上,一旦你读取了输入,它就消失了,再也不会被读取了。您可以推回一个 char
值,但这不足以知道还有多少数据有待读取,或重新读取整个流。
即使流是指向一个您可以查找的文件,您也不能在可移植的、严格符合 C 代码中使用 fseek()
/ftell()
来知道文件有多大是。
如果它是二进制流,则不能使用 fseek()
查找文件末尾 - 这是明确未定义的行为 per the C standard:
... A binary stream need not meaningfully support
fseek
calls with a whence value ofSEEK_END
.
Setting the file position indicator to end-of-file, as with
fseek(file, 0, SEEK_END)
, has undefined behavior for a binary stream ...
所以你不能在二进制流中便携地使用 fseek()
。
并且您不能使用 ftell()
来获取文本流的字节数。每 the C standard again:
For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
确实存在从 ftell()
返回的值与字节数完全不同的系统。
了解您可以从流中读取多少字节的唯一可移植、符合标准的方法是实际读取它们,您不能依赖能够再次读取它们。
如果要将整个流读入内存,则必须不断地重新分配内存,或者使用其他一些动态方案。
这是将流的全部内容读入内存的一种非常低效但可移植且严格符合标准的方法(为了算法清晰和防止垂直滚动条出现,省略了所有错误检查和头文件 - 它确实需要错误检查并需要正确的头文件):
// get input stream with `fopen()` or some other manner
FILE *input = ...
size_t count = 0;
char *data = NULL;
for ( ;; )
{
int c = fgetc( input );
if ( c == EOF )
{
break;
}
data = realloc( data, count + 1 );
data[ count ] = c;
count++;
}
// optional - terminate the data with a '[=10=]'
// to treat the data as a C-style string
data = realloc( data, count + 1 );
data[ count ] = '[=10=]';
count++;
无论流是什么,这都有效。
在 POSIX 风格的系统上,例如 Linux,您可以使用 fileno()
and fstat()
来获取文件的大小(同样,省略了所有错误检查和头文件) :
char *data = NULL;
FILE *input = ...
int fd = fileno( input );
struct stat sb;
fstat( fd, &sb );
if ( S_ISREG( sb.st_mode ) )
{
// sb.st_size + 1 for C-style string
char *data = malloc( sb.st_size + 1 );
data[ sb.st_size ] = '[=11=]';
}
// now if data is not NULL you can read into the buffer data points to
// if data is NULL, see above code to read char-by-char
// this tries to read the entire stream in one call to fread()
// there are a lot of other ways to do this
size_t totalRead = 0;
while ( totalRead < sb.st_size )
{
size_t bytesRead = fread( data + totalRead, 1, sb.st_size - totalRead, input );
totalRead += bytesRead;
}
以上内容也适用于 Windows。您也可能会得到 some compiler warnings or have to use _fileno()
, _fstat()
and struct _stat
。*
您可能还需要 define the S_ISREG()
macro on Windows:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
* 那是 _fileno()
、_fstat()
和 struct _stat
,没有超链接 underline-munge。