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;
}