VC 里面处理30多万行的txt文件时,速度为什么这么慢?

TandyT 2011-01-05 01:22:29
我用 CStdioFile 来打开文件,一次性把文件的内容读到内存中,然后对内存中的字符串进行处理。

原txt文件里面,是按行存储一些中文、英文字符,每行都有一个以上的 ‘|’ 字符,一共有30多万行。我想按照所在行的内容里面的 ‘|’字符个数来排序,也就是,把所有行中,每行只有一个 '|' 字符的,排在前面,每行有2个的,排在其次,其它的以此类推。

有点像“畸形”的金字塔,少的在上面,多的在下面,堆积起来。

我在VC 里面的做法是,把前面读取进来的(以文本方式读取进来)字符串,以换行符 CRLF 为标志,逐行读取到一个临时字符串 tem 中,然后,判断这个临时字符串tem 里面有 n 个 '|'字符,然后把它存储到专门存储 n 个 ‘|’字符的字符串里,当把内存中的30多万行都读取完后,也就分析完毕了,然后,依次 把存储一个‘|’字符、存储两个‘|’字符、存储三个 ‘|’字符、、、的字符串内容写入到一个新的 txt文件里面。

但是,发现这样做,速度很慢,我试过不用线程、用单线程、用多线程来分析,效果都一样,非常慢。(如果要分析的 txt 内容比较小,比如几百KB,当然没问题)。

各位大侠,应该用什么方法来实现我的这个功能呢?

谢谢指点!
...全文
1080 72 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
72 条回复
切换为时间正序
请发表友善的回复…
发表回复
skipwitit 2012-09-17
  • 打赏
  • 举报
回复
看到这个 感觉很欣慰啊
TandyT 2011-01-11
  • 打赏
  • 举报
回复
[Quote=引用 70 楼 snow_ice11111 的回复:]
引用 69 楼 tandyt 的回复:
引用 68 楼 snow_ice11111 的回复:
1 首先,CStdioFile尽管方便,但效率是很低的文件读取方法,原因是磁盘操作时寻道时间、潜伏时间等问题,所以还是一次性把所有文件内容全部读取到内存中,几十M的东西没必要用内存映射文件这种方法,何况我们要读取全部的内容,更没有用这个方法的必要;
2 其次,把内存中文件内容手工解析,按行符分解成一……
[/Quote]

我是从最基础的学起的,到CSDN 这里就是学习的,当然也希望某天可以帮助别人,哈哈,谢谢您
TandyT 2011-01-10
  • 打赏
  • 举报
回复
[Quote=引用 62 楼 saimen 的回复:]
更直接一点说:就是不要让临时字符串产生内存分配动作

在操作前,估算最大的可能需求空间,然后用GetBufferSetLength设置它可能的最大空间。
运算完成后ReleaseBuffer释放多的空间,得到完整的字符串。

速度就提上去了。
[/Quote]

嗯,学习了,谢谢指点,以后一定注意这个问题,谢谢
TandyT 2011-01-10
  • 打赏
  • 举报
回复
[Quote=引用 57 楼 varding 的回复:]
引用 54 楼 tandyt 的回复:

引用 53 楼 varding 的回复:
引用 50 楼 tandyt 的回复:

引用 47 楼 varding 的回复:
你的数据方便发上来么?我试试我那算法行不行


你留个联系方式,Email 或者QQ ,我发给你试试


deroculs2@126.com


您好,我已经把附件发到您邮箱了,谢谢


我用我的……
[/Quote]

嗯,我收到了,我这里没有 VS2010,我参考下您的代码试试,谢谢
HuWenjin 2011-01-10
  • 打赏
  • 举报
回复
更直接一点说:就是不要让临时字符串产生内存分配动作

在操作前,估算最大的可能需求空间,然后用GetBufferSetLength设置它可能的最大空间。
运算完成后ReleaseBuffer释放多的空间,得到完整的字符串。

速度就提上去了。

HuWenjin 2011-01-10
  • 打赏
  • 举报
回复

想要加快速度!

大量的数据处理过程中,杜绝 -> 内存分配
Snow_Ice11111 2011-01-10
  • 打赏
  • 举报
回复
[Quote=引用 69 楼 tandyt 的回复:]
引用 68 楼 snow_ice11111 的回复:
1 首先,CStdioFile尽管方便,但效率是很低的文件读取方法,原因是磁盘操作时寻道时间、潜伏时间等问题,所以还是一次性把所有文件内容全部读取到内存中,几十M的东西没必要用内存映射文件这种方法,何况我们要读取全部的内容,更没有用这个方法的必要;
2 其次,把内存中文件内容手工解析,按行符分解成一个个子串(动态创建char **数组吧),……
[/Quote]


你真的学到精华了么?!很怀疑啊,尽管如此,还是祝你好运!
yeah2000 2011-01-10
  • 打赏
  • 举报
回复
内存映射操作大文件速度很快的
pxhn111 2011-01-10
  • 打赏
  • 举报
回复
算法的问题把,还有就是和操作系统的处理机制有挂钩
TandyT 2011-01-10
  • 打赏
  • 举报
回复
[Quote=引用 68 楼 snow_ice11111 的回复:]
1 首先,CStdioFile尽管方便,但效率是很低的文件读取方法,原因是磁盘操作时寻道时间、潜伏时间等问题,所以还是一次性把所有文件内容全部读取到内存中,几十M的东西没必要用内存映射文件这种方法,何况我们要读取全部的内容,更没有用这个方法的必要;
2 其次,把内存中文件内容手工解析,按行符分解成一个个子串(动态创建char **数组吧),一个子串一行内容。另外动态创建两个同样大小的int类型数……
[/Quote]

嗯,楼上的说得不错,学习了,呵呵,谢谢
Snow_Ice11111 2011-01-10
  • 打赏
  • 举报
回复
1 首先,CStdioFile尽管方便,但效率是很低的文件读取方法,原因是磁盘操作时寻道时间、潜伏时间等问题,所以还是一次性把所有文件内容全部读取到内存中,几十M的东西没必要用内存映射文件这种方法,何况我们要读取全部的内容,更没有用这个方法的必要;
2 其次,把内存中文件内容手工解析,按行符分解成一个个子串(动态创建char **数组吧),一个子串一行内容。另外动态创建两个同样大小的int类型数组,其中一个用来存放每个子串中“|”的个数,原因:你也不想每次比较时都重新数一数“|”的个数吧,另一个留着后面用来保存子串的索引号;
3 再次,对前面这个int数组进行排序吧,按数值大小的顺序,把它们的数组索引依次保存到后面一个int数组中;
4 清空内存中文件内容,然后按后面一个数组中保存的子串索引,再逐一把子串添加到这块内存中;
5 最后,把该块内存的内容写入磁盘中。收工

提高效率的关键之处是:
1 只有一个读取文件的操作,同样的也仅有一个写入文件的操作;
2 每行“|”的个数我们只数一遍,绝不数第二遍;
3 排序的算法,这个我就不多说了。
TandyT 2011-01-10
  • 打赏
  • 举报
回复
65 楼那段程序,最后释放内存那里,漏写了: delete []presdata;
TandyT 2011-01-10
  • 打赏
  • 举报
回复
终于搞定了,
谢谢大家的帮助,特别是57楼的 varding
我用他的程序,测试过550万行的txt数据(195MB 左右),排序完毕并且写入结果txt中,用时为 13.5秒 左右,

不过他Email 给我的程序里面,写入txt文本的那种方法,不行,即使15MB 的txt处理完后,程序都会崩溃。

我修改了他的这段写入txt的方法,一次性写入文件。
下面是整个实现的代码:(我修改后的)
//-------------------------------------------------------

typedef vector<vector<_line_info>> lineArr;
void CTestGuiDlg::OnBnClickedOk()
{
int _start = GetTickCount();
CFile _file;
_file.Open("C:\\test.txt",CFile::modeRead);

//文件长度;
int _len = _file.GetLength();
char* pdata = new char[_len+1];
char* presdata= new char[_len+1];
//读文件;
_file.Read(pdata,_len);
pdata[_len] = 0;
_file.Close();

lineArr _line_arr;

_line_arr.resize(100); //假设最多一行有10个'|'

int cnt = 0;

_line_info _info;
int _head_pos = 0;
for(int i = 0;i < _len;)
{
if(pdata[i] == '\r') //回车;
{
i += 2;
_info.pos = _head_pos; //这一行的起点;
_info.len = i - _head_pos; //这一行的长度;
_line_arr[cnt-1].push_back(_info);

_head_pos = i; //下一行起点;
cnt = 0; //'|'计数清0
continue;
}
else if(pdata[i] == '|')
++cnt;
++i;
}
/*
//这段是 57楼那位朋友的写入文件的程序
//写文件;
CFile _file2;
_file2.Open("C:\\test保存的结果.txt",CFile::modeWrite);

//遍历并且写文件;
for (lineArr::iterator it = _line_arr.begin();it != _line_arr.end();++it)
{
for (vector<_line_info>::iterator it2 = it->begin();it2 != it->end();++it2)
{
_file2.Write(pdata + it2->pos,it2->len);
}
}
*/
//下面这个是我修改的,一次性写入,速度快,不用频繁的写入。
char *ptem=presdata;
//遍历复制到结果缓存区;
for (lineArr::iterator it = _line_arr.begin();it != _line_arr.end();++it)
{
for (vector<_line_info>::iterator it2 = it->begin();it2 != it->end();++it2)
{
memcpy(ptem,pdata + it2->pos,it2->len);
ptem+=it2->len;
}
}
CFile _file2;
_file2.Open("C:\\test保存的结果.txt",CFile::modeCreate|CFile::modeWrite);
_file2.Write(presdata,_len+1);
_file2.Close();

delete[] pdata;


//消耗时间;
int _end = GetTickCount();
int _time = _end - _start;

char buf[16];
sprintf(buf,"消耗时间:%d 毫秒",_time);
MessageBox(buf);
}

varding 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 50 楼 tandyt 的回复:]

引用 47 楼 varding 的回复:
你的数据方便发上来么?我试试我那算法行不行


你留个联系方式,Email 或者QQ ,我发给你试试
[/Quote]

deroculs2@126.com
TandyT 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 49 楼 lazy_2010 的回复:]
1、strread 使用 Find 函数(strchr) 而不是一个一个字符进行分析不好么?

2、test=test.Right(test.GetLength()-temnum-1); 这句话没必要,不要使用 CSting,修改成处理 LPCSTR 吧,这样使用 CString 就是在给自己找麻烦,文件越大越耗时间。或者你记录 position 使用 CString 的 Find(char,……
[/Quote]

哦,有道理,谢谢啦,改天试试,谢谢
TandyT 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 48 楼 xianglitian 的回复:]
引用 46 楼 tandyt 的回复:
如果不用字符串CString 来操作的话,那用 字符数组么? 数组的话,那就每行都要遍历来比较找出 ‘|’的个数了,

如果不用字符数组,那用什么比较啊?

如果用二进制格式读取进来处理,会不会快点?要是二进制的话,判断字符,岂不是麻烦?

CString内部是动态分配内存的
申请内存有可能会浪费时间
我就是建议一下
先看看是不是这方面的……
[/Quote]

嗯,好的,那我先试试,谢谢了
TandyT 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 47 楼 varding 的回复:]
你的数据方便发上来么?我试试我那算法行不行
[/Quote]

你留个联系方式,Email 或者QQ ,我发给你试试
varding 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 54 楼 tandyt 的回复:]

引用 53 楼 varding 的回复:
引用 50 楼 tandyt 的回复:

引用 47 楼 varding 的回复:
你的数据方便发上来么?我试试我那算法行不行


你留个联系方式,Email 或者QQ ,我发给你试试


deroculs2@126.com


您好,我已经把附件发到您邮箱了,谢谢
[/Quote]

我用我的方法试了下,如果只是数每一行的'|'个数并且按照这个排序,加上读写文件大约几秒,邮件已回请查收
验证码识别 2011-01-09
  • 打赏
  • 举报
回复
然后把它存储到专门存储 n 个 ‘|’字符的字符串里
--------------------------------------------

有可能是内存操作太频繁, 预先分配一个大的空间试试

string::reserve
TandyT 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 53 楼 varding 的回复:]
引用 50 楼 tandyt 的回复:

引用 47 楼 varding 的回复:
你的数据方便发上来么?我试试我那算法行不行


你留个联系方式,Email 或者QQ ,我发给你试试


deroculs2@126.com
[/Quote]

您好,我已经把附件发到您邮箱了,谢谢
加载更多回复(49)

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧