我有一个特定的txt文件(例如-dic.txt),其中的单词按以下顺序出现:
hello - ola - hiya n
chips - fries - frenchfries n
我需要将文件的内容读取到字符串数组的数组中:例如:
array[0] : [hello,ola,hiya]
array[1] : [chips,fries,frenchfries]
我曾想过使用strtok
将文件中的每一行拆分为字符串(在将整个文件复制为字符串并计算行数之后),但我不知道如何将每一行("hello - ola - hiya n"
)拆分为单词,并将每个数组存储到数组中(数组中的字符串数组)。
我曾考虑使用malloc
为每行单词分配内存,并将指向字符串数组的指针存储到数组中,但我很乐意收到任何建议。
从文件中读取行并将其拆分为标记的简单方法是使用fgets
读取行,然后使用strtok
将每行拆分为标记:
int main(int argc, char *argv[])
{
// Check for arguments and file pointer omitted
FILE *f = fopen(argv[1], "r");
for (;;) {
char line[80];
char *token;
if (fgets(line, 80, f) == NULL) break;
token = strtok(line, " -n");
while (token) {
// Do something with token, for example:
printf("'%s' ", token);
token = strtok(NULL, " -n");
}
}
fclose(f);
return 0;
}
只要文件中的所有行都小于80个字符,这种方法就可以了。它适用于每行可变数量的令牌。
您已经提到了处理行内存的问题。上面的例子假设内存处理是由每个字的数据结构完成的。(这不是示例的一部分,它只是打印令牌。)
您可以为每行malloc
内存,这比每行严格的字符限制更灵活,但最终会有很多分配。好处是你的单词不需要额外的内存,它们可以只是指向行的指针,但你必须为行正确分配内存,然后再释放。
如果你把整个文本文件读到一个连续的内存块中,你基本上就完成了内存存储,只要你让这个内存块和你的单词一样"活着":
char *slurp(const char *filename, int *psize)
{
char *buffer;
int size;
FILE *f;
f = fopen(filename, "r");
if (f == NULL) return NULL;
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
buffer = malloc(size + 1);
if (buffer) {
if (fread(buffer, 1, size, f) < size) {
free(buffer);
} else {
buffer[size] = ' ';
if (psize) *psize = size;
}
}
fclose(f);
return buffer;
}
有了这个内存块,您可以首先通过查找下一个换行来查找行,然后如上所述使用strtok
:
int main(int argc, char *argv[])
{
char *buffer; // contiguous memory chunk
char *next; // pointer to next line or NULL for last line
buffer = slurp(argv[1], NULL);
if (buffer == NULL) return 0;
next = buffer;
while (next) {
char *token;
char *p = next;
// Find beginning of the next line,
// i.e. the char after the next newline
next = strchr(p, 'n');
if (next) {
*next = ' '; // Null-terminate line
next = next + 1; // Advance past newline
}
token = strtok(p, " -n");
while (token) {
// Do something with token, for example:
printf("'%s' ", token);
token = strtok(NULL, " -n");
}
}
free(buffer); // ... and invalidate your words
return 0;
}
如果使用fscan
,则总是将找到的令牌复制到临时缓冲区,当将它们存储在字典结构中时,必须使用strcpy
再次复制它们。这是大量的复制。在这里,您读取并分配一次,然后使用指向块的指针。strtok
null终止令牌,因此您的区块是一个C字符串链。
将整个m文件读取到内存中通常不是一个好的解决方案,但在这种情况下,如果文件基本上是数据,这是有意义的。
(注意:所有关于内存的讨论都不会影响字典结构所需的内存、树和行列表中的节点或其他什么。它只是关于正确存储字符串。)
使用fgets
:
int eol(int c, FILE *stream) //given a char and the file, check if eol included
{
if (c == 'n')
return 1;
if (c == 'r') {
if ((c = getc(stream)) != 'n')
ungetc(c, stream);
return 1;
}
return 0;
}
int charsNumInLine(FILE *stream)
{
int position = ftell(stream);
int c, num_of_chars=0;
while ((c = getc(stream)) != EOF && !eol(c, stream))
num_of_chars++;
fseek(stream,position,SEEK_SET); //get file pointer to where it was before this function call
return num_of_chars;
}
void main()
{
//...
char *buffer;
int size;
while()
{
size=charsNumInLine(stream);
buffer = (char*)malloc( size*sizeof(char) );
fgets(buffer,sizeof(buffer),stream);
if (feof(stream) || ferror(stream) )
break;
// use strtok to separate words...
}
//...
}
另一种方法是使用fscanf(file,"%s",buff)
读取单词,然后使用上面的函数eol
查看何时到达换行符。