C中的ascii文件处理
ascii file processing in C
我很难理解你是如何在 c 中处理 ascii 文件的。打开文件和关闭文件或读取每行一个值的文件都没有问题。但是,当数据用字符分隔时,我真的不明白代码在更底层做了什么。
示例:我有一个文件,其中包含以逗号分隔的名称,如下所示:
"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER"
我创建了一个数组来存储它们:
char names[6000][20];
现在,我处理它的代码是 while (fscanf(data, "\"%s\",", names[index]) != EOF) { index++; }
代码在第一次迭代时执行,names[0] 包含整个文件。
如何分隔所有名称?
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int index = 0;
int nbNames;
while (fscanf(data, "\"%s\",", names[index]) != EOF) {
index++;
}
nbNames = index;
fclose(data);
printf("%d\n", index);
for (index=0; index<nbNames; index++) {
printf("%s \n", names[index]);
}
printf("\n");
return 0;
}
PS:我想这也可能是因为我的数组的数据结构。
如果你想要一个简单的解决方案,你可以使用fgetc
逐个字符地读取文件。由于文件中没有换行符,因此忽略引号并在找到逗号时移至下一个索引。
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int name_count = 0, current_name_ind = 0;
int c;
while ((c = fgetc(data)) != EOF) {
if (c == ',') {
names[name_count][current_name_ind] = '[=10=]';
current_name_ind = 0;
++name_count;
} else if (c != '"') {
names[name_count][current_name_ind] = c;
++current_name_ind;
}
}
names[name_count][current_name_ind] = '[=10=]';
fclose(data);
您可以使用易于使用的内置 strtok()
功能。
我使用 tok+1
而不是 tok
来省略第一个 " 和 strlen(tok) - 2
来省略最后一个 "。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int index = 0;
int nbNames;
char *str = (char*)malloc(120000*sizeof(char));
while (fscanf(data, "%s", str) != EOF) {
char *tok = strtok(str, ",");
while(tok != 0){
strncpy(names[index++], tok+1, strlen(tok)-2);
tok = strtok(0, ",");
}
}
nbNames = index;
fclose(data);
free(str); // just to free the memory occupied by the str variable in the heap.
printf("%d\n", index);
for (index=0; index<nbNames; index++) {
printf("%s \n", names[index]);
}
printf("\n");
return 0;
}
此外,参数120000只是文件中可以包含的最大字符数。正如你所说,它只是 6000 * 20。
“代码执行第一次迭代,names[0] 包含整个文件....,如何分隔所有名称?”
关于前几条陈述:
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
如果有6001个名字怎么办。或者其中一个名字超过 20 个字符?
或者,如果名称少于 6000 个怎么办?
关键是,通过一些努力来列举您列出的任务,并花一些时间映射出创建符合您标准的代码所需的信息,您可以创建更好的产品:以下内容来自你的 post:
- 在 c 中处理 ascii 文件
- 读取以字符分隔的文件内容
- 输入是一个逗号分隔的文件,还有其他分隔符
- 选择最适合解析可变大小文件的方法
正如您问题下的评论中所提到的,有一些方法可以创建您的算法,以便灵活地允许超长名称或可变数量的名称。这可以使用一些常用于解析文件的 C 标准函数来完成。 (虽然 fscanf()
有它的位置,但它不是将文件内容解析为数组元素的最佳选择。)
以下方法执行以下步骤来完成上面列举的用户需求
- 读取文件以确定元素的数量和最长元素
- 使用元素计数和最长元素使用 variable length array (VLA)
创建数组大小以包含文件的确切内容
- 创建将文件内容解析为数组的函数。 (使用这个technique of passing VLA as function argument。)
以下是一个完整的示例,说明如何实现其中的每一个,同时在适当的时候将任务分解为函数...
注意,下面的代码是使用以下输入文件测试的:
names.txt
"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER",
"Joseph","Bart","Daniel","Stephan","Karen","Beth","Marcia",
"Calmazzothoulumus"
.
//Prototypes
int count_names(const char *filename, size_t *count);
size_t filesize(const char *fn);
void populateNames(const char *fn, int longest, char arr[][longest]);
char *filename = ".\names.txt";
int main(void)
{
size_t count = 0;
int longest = count_names(filename, &count);
char names[count][longest+1];//VLA - See linked info
// +1 is room for null termination
memset(names, 0, sizeof names);
populateNames(filename, longest+1, names);
return 0;
}
//populate VLA with names in file
void populateNames(const char *fn, int longest, char names[][longest])
{
char line[80] = {0};
char *delim = "\",\n ";
char *tok = NULL;
FILE * fp = fopen(fn, "r");
if(fp)
{
int i=0;
while(fgets(line, sizeof line, fp))
{
tok = strtok(line, delim);
while(tok)
{
strcpy(names[i], tok);
tok = strtok(NULL, delim);
i++;
}
}
fclose(fp);
}
}
//passes back count of tokens in file, and return longest token
int count_names(const char *filename, size_t *count)
{
int len=0, lenKeep = 0;
FILE *fp = fopen(filename, "r");
if(fp)
{
char *tok = NULL;
char *delim = "\",\n ";
int cnt = 0;
size_t fSize = filesize(filename);
char *buf = calloc(fSize, 1);
while(fgets(buf, fSize, fp)) //goes to newline for each get
{
tok = strtok(buf, delim);
while(tok)
{
cnt++;
len = strlen(tok);
if(lenKeep < len) lenKeep = len;
tok = strtok(NULL, delim);
}
}
*count = cnt;
fclose(fp);
free(buf);
}
return lenKeep;
}
//return file size in bytes (binary read)
size_t filesize(const char *fn)
{
size_t size = 0;
FILE*fp = fopen(fn, "rb");
if(fp)
{
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
fclose(fp);
}
return size;
}
我很难理解你是如何在 c 中处理 ascii 文件的。打开文件和关闭文件或读取每行一个值的文件都没有问题。但是,当数据用字符分隔时,我真的不明白代码在更底层做了什么。
示例:我有一个文件,其中包含以逗号分隔的名称,如下所示:
"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER"
我创建了一个数组来存储它们:
char names[6000][20];
现在,我处理它的代码是 while (fscanf(data, "\"%s\",", names[index]) != EOF) { index++; }
代码在第一次迭代时执行,names[0] 包含整个文件。
如何分隔所有名称?
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int index = 0;
int nbNames;
while (fscanf(data, "\"%s\",", names[index]) != EOF) {
index++;
}
nbNames = index;
fclose(data);
printf("%d\n", index);
for (index=0; index<nbNames; index++) {
printf("%s \n", names[index]);
}
printf("\n");
return 0;
}
PS:我想这也可能是因为我的数组的数据结构。
如果你想要一个简单的解决方案,你可以使用fgetc
逐个字符地读取文件。由于文件中没有换行符,因此忽略引号并在找到逗号时移至下一个索引。
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int name_count = 0, current_name_ind = 0;
int c;
while ((c = fgetc(data)) != EOF) {
if (c == ',') {
names[name_count][current_name_ind] = '[=10=]';
current_name_ind = 0;
++name_count;
} else if (c != '"') {
names[name_count][current_name_ind] = c;
++current_name_ind;
}
}
names[name_count][current_name_ind] = '[=10=]';
fclose(data);
您可以使用易于使用的内置 strtok()
功能。
我使用 tok+1
而不是 tok
来省略第一个 " 和 strlen(tok) - 2
来省略最后一个 "。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
int index = 0;
int nbNames;
char *str = (char*)malloc(120000*sizeof(char));
while (fscanf(data, "%s", str) != EOF) {
char *tok = strtok(str, ",");
while(tok != 0){
strncpy(names[index++], tok+1, strlen(tok)-2);
tok = strtok(0, ",");
}
}
nbNames = index;
fclose(data);
free(str); // just to free the memory occupied by the str variable in the heap.
printf("%d\n", index);
for (index=0; index<nbNames; index++) {
printf("%s \n", names[index]);
}
printf("\n");
return 0;
}
此外,参数120000只是文件中可以包含的最大字符数。正如你所说,它只是 6000 * 20。
“代码执行第一次迭代,names[0] 包含整个文件....,如何分隔所有名称?”
关于前几条陈述:
char names[6000][20]; // an array to store 6k names of max length 19
FILE * data = fopen("./022names.txt", "r");
如果有6001个名字怎么办。或者其中一个名字超过 20 个字符? 或者,如果名称少于 6000 个怎么办?
关键是,通过一些努力来列举您列出的任务,并花一些时间映射出创建符合您标准的代码所需的信息,您可以创建更好的产品:以下内容来自你的 post:
- 在 c 中处理 ascii 文件
- 读取以字符分隔的文件内容
- 输入是一个逗号分隔的文件,还有其他分隔符
- 选择最适合解析可变大小文件的方法
正如您问题下的评论中所提到的,有一些方法可以创建您的算法,以便灵活地允许超长名称或可变数量的名称。这可以使用一些常用于解析文件的 C 标准函数来完成。 (虽然 fscanf()
有它的位置,但它不是将文件内容解析为数组元素的最佳选择。)
以下方法执行以下步骤来完成上面列举的用户需求
- 读取文件以确定元素的数量和最长元素
- 使用元素计数和最长元素使用 variable length array (VLA) 创建数组大小以包含文件的确切内容
- 创建将文件内容解析为数组的函数。 (使用这个technique of passing VLA as function argument。)
以下是一个完整的示例,说明如何实现其中的每一个,同时在适当的时候将任务分解为函数...
注意,下面的代码是使用以下输入文件测试的:
names.txt
"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER",
"Joseph","Bart","Daniel","Stephan","Karen","Beth","Marcia",
"Calmazzothoulumus"
.
//Prototypes
int count_names(const char *filename, size_t *count);
size_t filesize(const char *fn);
void populateNames(const char *fn, int longest, char arr[][longest]);
char *filename = ".\names.txt";
int main(void)
{
size_t count = 0;
int longest = count_names(filename, &count);
char names[count][longest+1];//VLA - See linked info
// +1 is room for null termination
memset(names, 0, sizeof names);
populateNames(filename, longest+1, names);
return 0;
}
//populate VLA with names in file
void populateNames(const char *fn, int longest, char names[][longest])
{
char line[80] = {0};
char *delim = "\",\n ";
char *tok = NULL;
FILE * fp = fopen(fn, "r");
if(fp)
{
int i=0;
while(fgets(line, sizeof line, fp))
{
tok = strtok(line, delim);
while(tok)
{
strcpy(names[i], tok);
tok = strtok(NULL, delim);
i++;
}
}
fclose(fp);
}
}
//passes back count of tokens in file, and return longest token
int count_names(const char *filename, size_t *count)
{
int len=0, lenKeep = 0;
FILE *fp = fopen(filename, "r");
if(fp)
{
char *tok = NULL;
char *delim = "\",\n ";
int cnt = 0;
size_t fSize = filesize(filename);
char *buf = calloc(fSize, 1);
while(fgets(buf, fSize, fp)) //goes to newline for each get
{
tok = strtok(buf, delim);
while(tok)
{
cnt++;
len = strlen(tok);
if(lenKeep < len) lenKeep = len;
tok = strtok(NULL, delim);
}
}
*count = cnt;
fclose(fp);
free(buf);
}
return lenKeep;
}
//return file size in bytes (binary read)
size_t filesize(const char *fn)
{
size_t size = 0;
FILE*fp = fopen(fn, "rb");
if(fp)
{
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
fclose(fp);
}
return size;
}