c - 如何快速读取和解析带有数字的文本文件(在 C 中)?

标签 c performance parsing readfile text-processing

最后一次更新:我同学用fread()把整个文件的三分之一读取成一个字符串,这样可以避免内存不足。然后处理这个字符串,将这个字符串分离到你的数据结构中。注意,你需要关心一个问题:在这个字符串的末尾,最后这几个字符可能不能组成一个整数。考虑一种检测这种情况的方法,以便您可以将这些字符与下一个字符串的前几个字符连接起来。 每个数字对应于数据结构中的不同变量。您的数据结构应该非常简单,因为每次将数据插入一个数据结构时,速度都非常慢。大部分时间花在将数据插入数据结构上。因此,处理这些数据最快的方法是:用fread()把这个文件读成一个字符串,把这个字符串分成不同的一维数组。 例如(只是一个例子,不是来 self 的项目),我有一个文本文件,如:

72     24      20
22     14      30
23     35      40
42     29      50
19     22      60
18     64      70
 .
 .
 .

每一行是一个人的信息。第一栏是这个人的年龄,第二栏是他的存款,第二栏是他妻子的年龄。 然后我们使用fread()将这个文本文件读入字符串,然后我使用stroke()来分离它(你可以使用更快的方法来分离它)。 不要使用数据结构来存储分离的数据! 我的意思是,不要这样做:

struct person
{
    int age;
    int deposit;
    int wife_age;
};
struct person *my_data_store;
my_data_store=malloc(sizeof(struct person)*length_of_this_array);
//then insert separated data into my_data_store

不要用数据结构来存储数据! 存储数据的最快方式是这样的:

int *age;
int *deposit;
int *wife_age;

age=(int*)malloc(sizeof(int)*age_array_length);
deposit=(int*)malloc(sizeof(int)*deposit_array_length);
wife_age=(int*)malloc(sizeof(int)*wife_array_length);
// the value of age_array_length,deposit_array_length and wife_array_length will be known by using `wc -l`.You can use wc -l to get the value in your C program
// then you can insert separated data into these arrays when you use `stroke()` to separate them.

第二次更新:最好的方法是使用freed()将文件的一部分读入一个字符串,然后将这些字符串分离到你的数据结构中。顺便说一下,不要使用任何可以将字符串格式化为整数的标准库函数,那样会很慢,比如 fscanf() 或 atoi(),我们应该编写自己的函数来将字符串转换为n 整数。不仅如此,我们还应该设计一个更简单的数据结构来存储这些数据。顺便说一句,我同学可以在7秒内读完这个1.7G的文件。有一种方法可以做到这一点。这种方式比使用多线程要好得多。我没有看到他的代码,看到他的代码后,我会第三次更新告诉你你怎么会这样。那将是我们类(class)结束两个月后。

更新:我用多线程来解决这个问题!!有用!注意:使用多线程时不要使用clock()来计算时间,所以我认为执行时间会增加。

我想澄清的一件事是,读取文件而不将值存储到我的结构中的时间大约为 20 秒。将值存储到我的结构中的时间约为 60 秒。 “读取文件时间”的定义包括读取整个文件并将值存储到我的结构中的时间。读取文件的时间=扫描文件+将值存储到我的结构中。因此,有一些更快储值的建议吗? (顺便说一句,我无法控制inout文件,它是我们教授生成的。我正在尝试使用多线程来解决这个问题,如果可行,我会告诉你结果。)

我有一个文件,大小是1.7G。 看起来像:

1   1427826
1   1427827
1   1750238
1   2
2   3
2   4
3   5
3   6
10  7
11  794106
.
.

继续。 它在文件中有大约一千万行。现在我需要读取这个文件并在 15 秒内将这些数字存储在我的数据结构中。 我曾尝试使用 freed() 来读取整个文件,然后使用 strtok() 来分隔每个数字,但它仍然需要 80 秒。如果我使用 fscanf(),它会更慢。我该如何加快速度?也许我们不能让它少于 15 秒。但是80秒阅读它太长了。如何以最快的速度阅读它?

这是我阅读代码的一部分:

int Read_File(FILE *fd,int round)
{
clock_t start_read = clock();


int first,second;
first=0;
second=0;


fseek(fd,0,SEEK_END);
long int fileSize=ftell(fd);
fseek(fd,0,SEEK_SET);
char * buffer=(char *)malloc(sizeof(char)*fileSize);
char *string_first;
long int newFileSize=fread(buffer,1,fileSize,fd);
char *string_second;
     while(string_first!=NULL)

    {
        first=atoi(string_first);

        string_second=strtok(NULL," \t\n");

        second=atoi(string_second);

        string_first=strtok(NULL," \t\n");

        max_num= first > max_num ? first : max_num ;
        max_num= second > max_num ? second : max_num ;
        root_level=first/NUM_OF_EACH_LEVEL;

        leaf_addr=first%NUM_OF_EACH_LEVEL;

        if(root_addr[root_level][leaf_addr].node_value!=first)
        {
            root_addr[root_level][leaf_addr].node_value=first;
            root_addr[root_level][leaf_addr].head=(Neighbor *)malloc(sizeof(Neighbor));
            root_addr[root_level][leaf_addr].tail=(Neighbor *)malloc(sizeof(Neighbor));

            root_addr[root_level][leaf_addr].g_credit[0]=1;

            root_addr[root_level][leaf_addr].head->neighbor_value=second;

           root_addr[root_level][leaf_addr].head->next=NULL;
            root_addr[root_level][leaf_addr].tail=root_addr[root_level][leaf_addr].head;
            root_addr[root_level][leaf_addr].degree=1;
        }
        else
        {
            //insert its new neighbor
            Neighbor *newNeighbor;
            newNeighbor=(Neighbor*)malloc(sizeof(Neighbor));

            newNeighbor->neighbor_value=second;

            root_addr[root_level][leaf_addr].tail->next=newNeighbor;
            root_addr[root_level][leaf_addr].tail=newNeighbor;

            root_addr[root_level][leaf_addr].degree++;
        }
        root_level=second/NUM_OF_EACH_LEVEL;

        leaf_addr=second%NUM_OF_EACH_LEVEL;
        if(root_addr[root_level][leaf_addr].node_value!=second)
        {
            root_addr[root_level][leaf_addr].node_value=second;
            root_addr[root_level][leaf_addr].head=(Neighbor *)malloc(sizeof(Neighbor));
            root_addr[root_level][leaf_addr].tail=(Neighbor *)malloc(sizeof(Neighbor));

            root_addr[root_level][leaf_addr].head->neighbor_value=first;

            root_addr[root_level][leaf_addr].head->next=NULL;
            root_addr[root_level][leaf_addr].tail=root_addr[root_level][leaf_addr].head;

            root_addr[root_level][leaf_addr].degree=1;

            root_addr[root_level][leaf_addr].g_credit[0]=1;
        }
        else
        {
            //insert its new neighbor
            Neighbor *newNeighbor;
            newNeighbor=(Neighbor*)malloc(sizeof(Neighbor));

            newNeighbor->neighbor_value=first;

            root_addr[root_level][leaf_addr].tail->next=newNeighbor;
            root_addr[root_level][leaf_addr].tail=newNeighbor;

            root_addr[root_level][leaf_addr].degree++;
        }
    }

最佳答案

一些建议:

a) 考虑将文件转换(或预处理)为二进制格式;目的是最小化文件大小并大大降低解析成本。我不知道您的值的范围,但是各种技术(例如使用一位来判断数字是小还是大并将数字存储为 7 位整数或 31 位整数)可以将文件减半IO(以及从磁盘读取文件的速度翻倍)并将解析成本降至几乎为零。 注意:为了获得最大效果,您首先要修改创建该文件的任何软件。

b) 在解析之前将整个文件读入内存是错误的。它使所需的 RAM 量(以及分配/释放的成本)加倍,并且对 CPU 缓存有不利影响。取而代之的是读取少量文件(例如 16 KiB)并处理它,然后读取下一 block 并处理它,依此类推;这样您就可以不断重复使用相同的小缓冲内存。

c) 对文件 IO 使用并行性。在处理文件的前一部分时读取文件的下一部分应该不难(通过使用 2 个线程或使用异步 IO)。

d) 为“邻居”结构预分配内存并从循环中删除大部分/所有 malloc() 调用。最好的情况是使用静态分配的数组作为池——例如Neighbor myPool[MAX_NEIGHBORS]; 其中 malloc() 可以替换为 &myPool[nextEntry++];。这减少/消除了 malloc() 的开销,同时还改进了数据本身的缓存局部性。

e) 使用并行来存储值。例如,您可以有多个线程,其中第一个线程处理 root_level % NUM_THREADS == 0 的所有情况,第二个线程处理 root_level % NUM_THREADS == 1 的所有情况> 等

有了以上所有(假设现代 4 核 CPU),我认为您可以将总时间(用于读取和存储)减少到 15 秒以内。

关于c - 如何快速读取和解析带有数字的文本文件(在 C 中)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29785412/

相关文章:

javascript - 如果函数被多次调用,在函数中缓存选择器是否性能更高?

java - 使用 HTMLunit 获取 ajax/javascript 内容

java - JSoup 从 html 文件中按顺序解析文本和链接

python - 扩展 python - swig,而不是 swig 或 Cython

c - strtok 未按预期工作,仅适用于前几次迭代

c - Arduino Pro 迷你启动画面

sql - 如何将表固定到 11G 中的 oracle dbms_shared_pool

python - 计算一个数组在另一个数组中没有重叠的出现次数

json - 通过Logstash将哈希数组转换为简单哈希

c - 如何检查 Linux 进程及其所有子进程何时退出?