如何快速读取和解析带有数字的文本文件(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
- 实验缓冲区大小
- 启动编译器优化。这类代码从优秀的代码生成中获益匪浅
#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