求从文本读取大量(数十万行)数据的最有效的方法?(建议就有分,在线等!!)
本人要打算从文本中读取大量的数据(有整形数和字符串型)对程序中的数据进行赋值,但我用的方法感觉时间太长。
方法很常规:从文件流中读取数据(用的标准库函数getline 读取数据到一个字符串缓冲区str中,然后在将str导入到字符串流ss中,在然后从ss中对程序中的数据进行赋值。
可这样下来,需要几分钟。感觉不爽。
在此,望大家提些建议,使读取速度最大化.
[解决办法]
用内存文件映射?
[解决办法]
这个我也没用过,主要是三个API: CreateFile/CreateFileMapping/MapViewOfFile,把你需要的文件映射到程序的地址空间里,得到一个指针指向文件的某个位置。。。
[解决办法]
multithread + filemapping
[解决办法]
换成速度更快的计算机。哈哈
[解决办法]
LZ是每读取一行然后马上再处理数据的吗?
感觉那样用getline好慢啊,我曾经用getline这样处理数据,但感觉还不如直接连续一次读十几行或者更多行再来处理更快,,我测试的效率在没有优化时候好象是5倍
[解决办法]
首先可以考虑优化的是是否可采取二进制文件方式读写,将手机号码以及通讯录记录为相关的
C语言结构比如:
typedef struct tagSMinfo
{
T_Addr cpn ; //手机号码
T_AddBook bk; //通信录
}
写入数据时采取二进制写入,读取数据时采取二进制读取,此类方法的限制条件是结构体必须定长,这一点并不难做到,这样可以进行块读并且减少字符串解析过程。
上面的方法能够提高一定的性能,如果还不能够满足要求,那么可以考虑有TX提出的多线程
以及内存映射文件。
[解决办法]
如果速度确实有问题,可以考虑二进格式存储。
[解决办法]
怎么会差不多? 虽然我没有实际测试,但是二进制IO与getline还是有不小差别的,getline每次都需要识别到行结尾字符来区分一行。
二进制是块读而且无需数据转换,一次你可以读一个记录,也可以读若干个记录,这些都是
可以调整的。vector的使用方式也是需要考虑的,因为你加载的数据量很大,所以为了防止
vector频繁的内存deallocate和allocate,可以预先保留足够的内存空间。
使用数据库也是可以的,但是要看如何设计了,通讯录大小不等,没有必要将通信录卢和
用户信息放在一个表里,手机号码作为主关键字建立一个用户号码表,表中含用户手机号码,
以及通信录ID,通信录ID已外键形式关联到另外一个通信录表,每个通信录表的一行记录表示属于某个用户的通信录信息,这样你就可能存储变长的通信录。
代码不多的话可以贴上来,大家一起看看。
[解决办法]
线程异步处理:一个处理线程,一个读取线程,在加个循环队列可以解决问题.
[解决办法]
VC++中使用内存映射文件处理大文件
http://blog.csdn.net/yaosan/archive/2006/09/10/1203886.aspx
包括原理,以及参考代码,
楼主先看看吧。
[解决办法]
数十万行 大约 多大
10兆一下 直接装入内存
10兆以上 用 缓冲的方式 读 每次 读 128k 到内存
总之减少 io 函数 的调用 次数
[解决办法]
你看看 这个文件
百万行的数据处理 非常快
[解决办法]
比较疯狂,为什么不用数据库?
哪怕是用开源的MySql呢?
[解决办法]
在linux 下,我这边文本话单格式话,大概160w行,就用getline()大概几分钟吧。
[解决办法]
怎么会要那么长时间呢?
我用多线程处理800多个象你那样的文件,大概25分钟就完成了,环境是Linux.
我是用getline边读边处理的
[解决办法]
我处理的话单文件每个话单大概有80个左右字段,处理中用到了VECTOR,你也可以尝试用下MULTIMAP。
[解决办法]
http://www.yesky.com/20030117/1649013.shtml
[解决办法]
几十万行文件不一定大, 看你每行有多少数据。
如果每行平均6个字节的话。 1000万行的fgets读取只需要1秒。 on avarage server. 重定向为stdin.
[解决办法]
你可以使用两个缓冲区,一个读,一个拷贝出来处理,不过这样就要使用多线程
或者就使用内存映射文件来处理
上面大家也都说了,关键还是要降低I/O操作,把操作尽量放到内存中来做。
[解决办法]
建议使用数据库,或者 对文本格式作调整,比如 用户之间用分隔符号,用多线程方式,
单独一个读取线程,这样一次可以多读一些,然后字符串处理成你的结构,再给你的主进程去处理。
[解决办法]
建立内存buffer
[解决办法]
这么大的文件,一般来说不会一次性需要全部的内容的。都是需要哪个部分就读取那个部分。不知道lz具体需要实现一个什么样的程序。如果确实需要一次性全部读入,那么我建议,可以使用平台特有的办法。linux我不是很熟。在Windows下有映射文件以及overlapped I/O。可以使用overlapped I/O在发出I/O之后,处理上一次I/O的数据。这样读文件的同时能够处理数据,能够达到很好的效果。
[解决办法]
关键读入之后又 "导入到字符串流ss ",也就是他每读一行要建立一个字符流对象.读下一行虽然还是给这个流对象,但先那行数据要析构.频繁的构造析构对象很花时间的.
[解决办法]
学习中
[解决办法]
内存文件映射
[解决办法]
呵呵,流不流应该不时关键,关键是减少IO,比如一次读10字节,一共读10次,远比一次读100字节慢,
所以,最好别getline,可以考虑一次读取4M的文件数据,然后再内存中分行生成对象.
[解决办法]
LZ你那个ss用来干什么,没有看见使用的地方。
[解决办法]
楼上说得不错,ss只看到不断的构造清除,却没有实际用.
要不,楼主贴几行实例数据,和你的结构定义,空了我试着写一个看.
[解决办法]
我前几天也遇到了同样的问题,其实读取文件的速度倒是可以接受,只是从文本文件中读出字符串再转换为其他数据类型是最浪费时间的操作。
我最后把文本文件的内容读出来之后再写入一个二进制文件中,以后就去操作维护此二进制文件,这样就避免了数据类型间的转换,省去了大部分的时间,需要文本文件的时候可以由二进制文件导出,也不需要什么时间。
[解决办法]
struct UserInfo //这里是放在通讯录中的号码信息
{
int userId;
int userType;
};
struct UserList
{
int userId;
int userType;
int numOfSend;
vector < UserInfo > userPhBook;
};//
结构的所有字段,都是int,怎么会保存为txt文件呢,全部存为2进制文件,输入输出不需要转换.速度应可接受了.vector字段预分配较长内存. 还可以在vector数据段前保存个数,按个数分配内存.
此外,注意手机号一为11位,int类型保存不下.
[解决办法]
struct UserInfo //
{
int userId;
int userType;
UserInfo(int id,int type):userId(id),userType(type){}
};
//UserInfo::UserInfo(int id,int type):userIf(id),userType(type){}
struct UserList
{
int userId;
int userType;
int numOfSend;
vector <UserInfo> userPhBook;
};//
void SaveFile(UserList& user,ofstream & out)
{
out.write((const char*)&user,sizeof(int)*3);
int vec_size=user.userPhBook.size();
out.write((const char*)&vec_size,sizeof(int));
out.write((const char*)user.userPhBook.begin(),
vec_size*sizeof(UserInfo));
//UserInfo temp;
//for(int i=0;i <vec_size;++i)
//{
//}
}
void ReadFile(UserList& user,ifstream & input)//之所以从外面引用输入流
//和读取后的存储目标,是不想使用全局变量.应避免使用全局变量
{
input.read(( char*)&user,sizeof(int)*3);//读到前面3个字段
int vec_size;
input.read(( char*)&vec_size,sizeof(int));
user.userPhBook.reserve(vec_size);//预分配内存
UserInfo temp(0,0);
for(int i=0;i <vec_size;++i)
{//虽然频繁从流读取数据,但并不会频繁读取磁盘
//文件流是用缓冲区的形式,相当于我们频繁读内存
input.read(( char*)&temp,sizeof(UserInfo));
user.userPhBook.push_back(temp);
}
}
#define USERCOUNT 1
int main()
{
UserList user;//=new UserList[USERCOUNT];
user.userId=100001;
user.userType=2;
user.numOfSend =13910004;
user.userPhBook.push_back(UserInfo(200005,3));
user.userPhBook.push_back(UserInfo(200008,2));
user.userPhBook.push_back(UserInfo(200325,3));
ofstream output( "idBase.dat ", ios::binary);
SaveFile(user,output);
output.close();
//2 进制方式保存的数据
//用记事本打开是乱码
ifstream input( "idBase.dat ", ios::binary);
UserList *puser=new UserList[USERCOUNT];
for(int i=0;i <USERCOUNT;++i)
{
ReadFile(puser[i],input);
}
cout < <puser[0].userId < < " \n ";
cout < <puser[0].userType < < " \n ";
cout < <puser[0].numOfSend < < " \n ";
for(vector <UserInfo> ::iterator iter=puser[0].userPhBook.begin();
iter!=puser[0].userPhBook.end();++iter)
{
cout < <iter-> userId < < " " < <iter-> userType < <endl;
}
return 0;
}
[解决办法]
要求你的idBase.txt重新用2进制方式生成,且不能直接用记事本查看