如何快速读取和解析带有数字的文本文件(C语言)?

如何快速读取和解析带有数字的文本文件(C语言)?,c,performance,parsing,readfile,text-processing,C,Performance,Parsing,Readfile,Text Processing,上次更新:我的同学使用fread()将整个文件的三分之一读入字符串,这样可以避免内存不足。然后处理这个字符串,将这个字符串分离到您的数据结构中。注意,您需要注意一个问题:在这个字符串的末尾,最后几个字符可能不能组成一个整数。考虑一种检测这种情况的方法,以便可以将这些字符与下一个字符串的前几个字符连接起来。 每个数字对应于数据结构中的不同变量。您的数据结构应该非常简单,因为每次将数据插入一个数据结构时,速度都非常慢。大部分时间都花在将数据插入数据结构上。因此,处理这些数据的最快方法是:使用frea

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

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.
不要使用数据结构来存储数据! 存储数据的最快方法如下:

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文件。有一种方法可以做到这一点。这种方法比使用多线程要好得多。我还没有看到他的代码,在我看到他的代码后,我会第三次更新,告诉你他是如何做到这一点的。那将是我们课程结束后两个月

更新:我使用多线程来解决这个问题!!它起作用了!注意:在使用多线程时,不要使用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++;
        }
    }

我的建议是形成一个处理管道并对其进行线程处理。读取文件是一项受I/O限制的任务,而解析文件则受CPU限制。它们可以同时并行完成。

有几种可能性。你必须进行实验

  • 利用你的操作系统给你的。如果是Windows,请签出。这样,当Windows内核填充另一个缓冲区时,您的计算可以继续解析一个充满数据的缓冲区。然后切换缓冲区并继续。这与@Neal的建议有关,但缓冲开销较小。Windows通过DMA通道直接将数据存储在缓冲区中。禁止复制。如果是Linux,请查看。在这里,操作系统使用虚拟内存硬件或多或少地完成了Windows的重叠操作

  • 编码您自己的整数转换。这可能比按整数进行clib调用快一点

下面是示例代码。您希望绝对限制比较的数量

// Process one input buffer.
*end_buf = ' '; // add a sentinel at the end of the buffer
for (char *p = buf; p < end_buf; p++) {
  // somewhat unsafe (but fast) reliance on unsigned wrapping
  unsigned val = *p - '0';
  if (val <= 9) {
    // Found start of integer.
    for (;;) {
      unsigned digit_val = *p - '0';
      if (digit_val > 9) break;
      val = 10 * val + digit_val;
      p++;
    }
    ... do something with val
  }
}
//处理一个输入缓冲区。
*end_buf=“”;//在缓冲区的末尾添加一个哨兵
对于(char*p=buf;p
  • 不要对每条记录调用
    malloc
    一次。您应该一次分配多个结构的块

  • 实验缓冲区大小

  • 启动编译器优化。这类代码从优秀的代码生成中获益匪浅


是的,标准库转换函数的速度惊人地慢

如果可移植性不是问题,我会对文件进行内存映射。然后,可以使用类似以下C99代码(未测试)的代码来解析整个内存映射:

#include <stdlib.h>
#include <errno.h>

struct pair {
    unsigned long  key;
    unsigned long  value;
};

typedef struct {
    size_t       size; /* Maximum number of items */
    size_t       used; /* Number of items used */
    struct pair  item[];
} items;

/* Initial number of items to allocate for */
#ifndef ITEM_ALLOC_SIZE
#define ITEM_ALLOC_SIZE 8388608
#endif

/* Adjustment to new size (parameter is old number of items) */
#ifndef ITEM_REALLOC_SIZE
#define ITEM_REALLOC_SIZE(from) (((from) | 1048575) + 1048577)
#endif 

items *parse_items(const void *const data, const size_t length)
{
    const unsigned char       *ptr = (const unsigned char *)data;
    const unsigned char *const end = (const unsigned char *)data + length;
    items                  *result;
    size_t                  size = ITEMS_ALLOC_SIZE;
    size_t                  used = 0;
    unsigned long           val1, val2;

    result = malloc(sizeof (items) + size * sizeof (struct pair));
    if (!result) {
        errno = ENOMEM;
        return NULL;
    }

    while (ptr < end) {

        /* Skip newlines and whitespace. */
        while (ptr < end && (*ptr == '\0' || *ptr == '\t' ||
                             *ptr == '\n' || *ptr == '\v' ||
                             *ptr == '\f' || *ptr == '\r' ||
                             *ptr == ' '))
            ptr++;

        /* End of data? */
        if (ptr >= end)
            break;

        /* Parse first number. */
        if (*ptr >= '0' && *ptr <= '9')
            val1 = *(ptr++) - '0';
        else {
            free(result);
            errno = ECOMM; /* Bad data! */
            return NULL;
        }
        while (ptr < end && *ptr >= '0' && *ptr <= '9') {
            const unsigned long old = val1;
            val1 = 10UL * val1 + (*(ptr++) - '0');
            if (val1 < old) {
                free(result);
                errno = EDOM; /* Overflow! */
                return NULL;
            }
        }

        /* Skip whitespace. */
        while (ptr < end && (*ptr == '\t' || *ptr == '\v'
                             *ptr == '\f' || *ptr == ' '))
            ptr++;
        if (ptr >= end) {
            free(result);
            errno = ECOMM; /* Bad data! */
            return NULL;
        }

        /* Parse second number. */
        if (*ptr >= '0' && *ptr <= '9')
            val2 = *(ptr++) - '0';
        else {
            free(result);
            errno = ECOMM; /* Bad data! */
            return NULL;
        }
        while (ptr < end && *ptr >= '0' && *ptr <= '9') {
            const unsigned long old = val2;
            val1 = 10UL * val2 + (*(ptr++) - '0');
            if (val2 < old) {
                free(result);
                errno = EDOM; /* Overflow! */
                return NULL;
            }
        }
        if (ptr < end) {
            /* Error unless whitespace or newline. */
            if (*ptr != '\0' && *ptr != '\t' && *ptr != '\n' &&
                *ptr != '\v' && *ptr != '\f' && *ptr != '\r' &&
                *ptr != ' ') {
                free(result);
                errno = ECOMM; /* Bad data! */
                return NULL;
            }

            /* Skip the rest of this line. */
            while (ptr < end && *ptr != '\n' && *ptr != '\r')
                ptr++;
        }

        /* Need to grow result? */
        if (used >= size) {
            items *const old = result;

            size = ITEMS_REALLOC_SIZE(used);
            result = realloc(result, sizeof (items) + size * sizeof (struct pair));
            if (!result) {
                free(old);
                errno = ENOMEM;
                return NULL;
            }
        }

        result->items[used].key = val1;
        result->items[used].value = val2;
        used++;
    }

    /* Note: we could reallocate result here,
     *       if memory use is an issue.
    */

    result->size = size;
    result->used = used;

    errno = 0;
    return result;
}
#包括
#包括
结构对{
无符号长密钥;
无符号长值;
};
类型定义结构{
尺寸\u t尺寸;/*最大项目数*/
使用的尺寸;/*使用的项目数量*/
结构对项[];
}项目;
/*要分配的初始项目数*/
#ifndef项目分配尺寸
#定义项目A