我用 CStdioFile 来打开文件,一次性把文件的内容读到内存中,然后对内存中的字符串进行处理。原txt文件里面,是按行存储一些中文、英文字符,每行都有一个以上的 ‘|’ 字符,一共有30多万行。我想按照所在行的内容里面的 ‘|’字符个数来排序,也就是,把所有行中,每行只有一个 '|' 字符的,排在前面,每行有2个的,排在其次,其它的以此类推。有点像“畸形”的金字塔,少的在上面,多的在下面,堆积起来。我在VC 里面的做法是,把前面读取进来的(以文本方式读取进来)字符串,以换行符 CRLF 为标志,逐行读取到一个临时字符串 tem 中,然后,判断这个临时字符串tem 里面有 n 个 '|'字符,然后把它存储到专门存储 n 个 ‘|’字符的字符串里,当把内存中的30多万行都读取完后,也就分析完毕了,然后,依次 把存储一个‘|’字符、存储两个‘|’字符、存储三个 ‘|’字符、、、的字符串内容写入到一个新的 txt文件里面。但是,发现这样做,速度很慢,我试过不用线程、用单线程、用多线程来分析,效果都一样,非常慢。(如果要分析的 txt 内容比较小,比如几百KB,当然没问题)。各位大侠,应该用什么方法来实现我的这个功能呢? 谢谢指点!
我那个txt文件一共才 20多兆。。逻辑上我也不晓得哪里出错了算法方面,就是对每一行进行遍历,然后统计 ‘|’ 出现的次数,根据这个次数,把这一行添加到相应的字符串空间中。其他的,实在想不出哪里错了
代码在记事本里写的,你自己测试下vector<vector<char*>> _line_arr;_line_arr.reserve(10); //假设最多一行有10个'|'int cnt = 0;
char* pstart = data; //data是你读取出来的数据,filelen是数据长度
for(int i = 0;i < filelen;)
{
if(data[i] == '\r') //回车;
{
_line_arr[cnt-1].pushback(pstart); //把这一行的指针放到_line_arr数组里去
data[i] = 0; //把换行符变成0,这样pstart指向的就是一个字符串
i += 2;
pstart = data + i; //下一行 cnt == 0;
continue;
}
else if(data[i] == '|')
++cnt;
++i;
}//遍历vector写文件
没进入死循环,因为我测试过,差不多一个小时,才能把那个 txt 文本排序完成,写入新的文本中,如果进入死循环,是不可能完成的。
{
//---------------------------------------------------------
//BeginWaitCursor();
//===========================================================
CString strtem="",te="";
CStringList *list2=new CStringList,*list3=new CStringList,*list4=new CStringList,*list5=new CStringList,*list6=new CStringList,*list7=new CStringList,*list8=new CStringList;
int num=0,index=0;
int fileindex=0,totalnum=0;
//========================================================================
char *ttt=NULL;
CString strread;
BOOL bRet=FALSE;
CStdioFile filein,fileout; if (filein.Open(filepath,CFile::modeReadWrite | CFile::typeText))
{
do{
bRet = filein.ReadString(strread); //一次读取一行
if (strread.IsEmpty())
{
break;
}
// 串处理 .Find; .TrimLeft .TrimRight .Left
ttt=(LPSTR)(LPCTSTR)strread;
for (int k=0;k<strread.GetLength();k++)
{
if (ttt[k]=='|')
{
num++;
}
} switch (num)
{
case 1:
list2->AddTail(strread);
break;
case 2:
list3->AddTail(strread);
break;
case 3:
list4->AddTail(strread);
break;
case 4:
list5->AddTail(strread);
break;
case 5:
list6->AddTail(strread);
break;
case 6:
list7->AddTail(strread);
break;
case 7:
list8->AddTail(strread);
break;
default:
break;
}
num=0;
// 计算值
}while(bRet);
filein.Close();
}
else
{
MessageBoxA("打开文件失败!",NULL,MB_OK);
return;
}
// 把结果复制到 strtem
fileindex=0;
if (!list2->IsEmpty()||!list3->IsEmpty()||!list4->IsEmpty()||!list5->IsEmpty()||!list6->IsEmpty()||!list7->IsEmpty()||!list8->IsEmpty())
{
while (fileindex<list2->GetCount())
{
te = list2->GetAt(list2->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list3->GetCount())
{
te = list3->GetAt(list3->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list4->GetCount())
{
te = list4->GetAt(list4->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list5->GetCount())
{
te = list5->GetAt(list5->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list6->GetCount())
{
te = list6->GetAt(list6->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list7->GetCount())
{
te = list7->GetAt(list7->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
fileindex=0;
while (fileindex<list8->GetCount())
{
te = list8->GetAt(list8->FindIndex(fileindex++));
if (te.IsEmpty())
{
break;
}
else
{
strtem+=te;
strtem+='\n';
}
}
} //保存结果到txt
if (fileout.Open(strSavePath,CFile::modeCreate|CFile::modeReadWrite | CFile::typeText))
{
fileout.WriteString(strtem);
fileout.Close();
}
else
{
MessageBoxA("创建保存文件失败!","提示!",MB_OK);
return;
}
///////---------------------------------另外的方法按行读取txt内容--------------------------------------///////// /*
//通过Notpad++ 看所有字符,知道行末换行符为 CRLF
//换行符
carriage return = \r
line feed = \n
*/
delete list2;
delete list3;
delete list4;
delete list5;
delete list6;
delete list7;
delete list8;
}
你并没有把所有内容全都读到内存
而是一次读一行
bRet = filein.ReadString(strread); //一次读取一行问题也就出在这
DWORD WINAPI THREADPRO(LPVOID data)
{
//===========================================================
CString strtem="",te="";
CString list_noconvert="",m2="",m3="",m4="",m5="",m6="",m7="",m8="";
DWORD num_noconvert=0;
int num=0,index=0;
int fileindex=0,totalnum=0;
//========================================================================
char *buffer=(char *)data;
char *ttt=NULL;
CString strread;
BOOL bRet=FALSE;
///=====================================================
CString test="",sub="";
test.Format(buffer);
int temnum=0;
for (DWORD i=0;i<pDlg->linenum;i++)
{
temnum=test.FindOneOf(_T("\r\n"));
sub.Format(test.Left(temnum));
//----------------------------------------------------------------
// 串处理 .Find; .TrimLeft .TrimRight .Left
ttt=(LPSTR)(LPCTSTR)sub;
for (int k=0;k<sub.GetLength();k++)
{
if (ttt[k]=='|')
{
num++;
}
}
//pDlg->MessageBoxA(sub,"提示!",MB_OK);
switch (num)
{
case 1:
//list2->AddTail(sub);
m2+=sub;
m2+='\n';
break;
case 2:
m3+=sub;
m3+='\n';
//list3->AddTail(sub);
break;
case 3:
m4+=sub;
m4+='\n';
//list4->AddTail(sub);
break;
case 4:
m5+=sub;
m5+='\n';
//list5->AddTail(sub);
break;
case 5:
m6+=sub;
m6+='\n';
//list6->AddTail(sub);
break;
case 6:
m7+=sub;
m7+='\n';
//list7->AddTail(sub);
break;
case 7:
m8+=sub;
m8+='\n';
//list8->AddTail(sub);
break;
default:
break;
}
num=0;
// 计算值 //----------------------------------------------------------------
test=test.Right(test.GetLength()-temnum-1);
} if (finish_1&&finish_2&&finish_3&&finish_4&&finish_5&&finish_6&&finish_7)
{
finish_=TRUE;
myres=m1+m2+m3+m4+m5+m6+m7;
}
return 0;}
尤其是字符串比较长的时候
你可以考注释掉某些语句再运行下
看看什么语句耗时比较严重
申请内存有可能会浪费时间
我就是建议一下
先看看是不是这方面的问题
而且字符在内存里本来就是数
没什么麻烦的
你留个联系方式,Email 或者QQ ,我发给你试试
--------------------------------------------有可能是内存操作太频繁, 预先分配一个大的空间试试string::reserve
运算完成后ReleaseBuffer释放多的空间,得到完整的字符串。速度就提上去了。
谢谢大家的帮助,特别是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);
}
2 其次,把内存中文件内容手工解析,按行符分解成一个个子串(动态创建char **数组吧),一个子串一行内容。另外动态创建两个同样大小的int类型数组,其中一个用来存放每个子串中“|”的个数,原因:你也不想每次比较时都重新数一数“|”的个数吧,另一个留着后面用来保存子串的索引号;
3 再次,对前面这个int数组进行排序吧,按数值大小的顺序,把它们的数组索引依次保存到后面一个int数组中;
4 清空内存中文件内容,然后按后面一个数组中保存的子串索引,再逐一把子串添加到这块内存中;
5 最后,把该块内存的内容写入磁盘中。收工提高效率的关键之处是:
1 只有一个读取文件的操作,同样的也仅有一个写入文件的操作;
2 每行“|”的个数我们只数一遍,绝不数第二遍;
3 排序的算法,这个我就不多说了。
我是从最基础的学起的,到CSDN 这里就是学习的,当然也希望某天可以帮助别人,哈哈,谢谢您