thackit 发表于 2009-4-11 22:06:37

请教提取文本文件中某段内容的最快方法?

我想要从文本文件中提取某段内容,我现在的实现方法要不断读取文件,运行速度很慢,请教有没有更快的算法。

我的思路是:逐行读取该文件,当那行内容等于某值时,记录下起始行号;再继续往下读取,当某行内容等于另一个值时,记录下终止行号;用循环从起始行号到终止行号再次读取该文件,不断累加行内容直到结束,再写入文件。代码如下(尚未完全实现):
$num = 1
$file = FileOpen("test.txt", 0)
While 1
      $line = FileReadLine($file, $num)
      If @error = -1 Then ExitLoop
      $num = $num + 1
      If $line = " ===================================" Then ExitLoop                ;查找内容是仅有值
Wend
While 1
      $line = $line & @CRLF & FileReadLine($file, $num)
      If @error = -1 Then ExitLoop
      $num = $num + 1
Wend
FileClose($file)
$filecon = $line
$file = FileOpen("test.txt", 1)
FileWrite($file, $filecon)
FileClose($file)

[ 本帖最后由 thackit 于 2009-4-12 15:31 编辑 ]

大绯狼 发表于 2009-4-11 22:54:01

第一次的时候全部读到数组里 在内存中使用就会快很多

sensel 发表于 2009-4-11 23:55:49

读一次,放个开始标志。读取内容扔进数组,等于结束行时退出循环。数组有内容时用_ArrayToString组合成文件内容,然后写入文件。
另外,如果你要写入的文件与读取文件不同,你可以同时打开2个文件,这边读那边写。这样做的好处是不需要通过数组,节约内存使用。

Local $File, $Line, $iFlag = 0, $aResult

$File = FileOpen("test.txt", 0)
If $File = -1 Then Exit

While 1
        $Line = FileReadLine($File)
        If @error Then ExitLoop
        If $iFlag = 0 AND $Line = "开始行" Then $iFlag = 1
        If $iFlag = 0 Then ContinueLoop
        _ArrayAdd($aResult, $Line)
        If $Line = "结束行" Then ExitLoop
WEnd

FileClose($File)

If UBound($aResult) > 1 Then FileWrite("test.txt", _ArrayToString($aResult, @CRLF, 1))


[ 本帖最后由 sensel 于 2009-4-12 00:05 编辑 ]

sensel 发表于 2009-4-12 00:02:23

还有个方法是暴力型。把整个文件读入一个变量,再截取从开始行到结束行的字符串,然后写入文件。缺点是因为要读取整个文件,文件很大时速度太慢。

sensel 发表于 2009-4-12 00:18:37

与上面那段代码结果相同,但是经过速度优化。原因是_ArrayAdd操作用到了ReDim语句,但ReDim是很占CPU时间的。在我的机器上,当使用ReDim超过2000次时,可以明显感觉到速度下降。
这里参考新版的_FileListToArray,将ReDim操作减少为原来的1/1000来提升运行速度。缺点是代码稍稍复杂了一点。

Local $File, $Line, $iFlag = 0, $aResult

$File = FileOpen("test.txt", 0)
If $File = -1 Then Exit

While 1
        $Line = FileReadLine($File)
        If @error Then ExitLoop
        If $iFlag = 0 AND $Line = "开始行" Then $iFlag = 1
        If $iFlag = 0 Then ContinueLoop

        $aResult += 1
        If UBound($aResult) <= $aResult Then ReDim $aResult
        $aResult[$aResult] = $Line

        If $Line = "结束行" Then ExitLoop
WEnd

ReDim $aResult[$aResult + 1]
FileClose($File)

If UBound($aResult) > 1 Then FileWrite("test.txt", _ArrayToString($aResult, @CRLF, 1))

thackit 发表于 2009-4-12 10:04:28

感谢 sensel 的帮助。

不过我用楼上的代码测试运行速度,只比顶楼的快一点点,顶楼2.7xxs vs 楼上2.6xxs。

我的目标是0.xxs:face (29):

不知道正则表达式能不能快很多?

cnsnc 发表于 2009-4-12 10:52:59

要速度就把文件全部读入内存
$text=FileRead("test.txt")
$index1=StringInStr("开始字符串",$text)
$index2=StringInStr("结束字符串",$text)
$text1=StringMid($text,$index1,$index2-$index1)
FileWrite("text1.txt",$text1)
;$text1不包含结束字符串

[ 本帖最后由 cnsnc 于 2009-4-12 10:56 编辑 ]

sensel 发表于 2009-4-12 16:26:37

影响运行速度的关键在于磁盘读取速度和截取内容在文件中的位置、行数,大量的磁盘读操作必然带来运行速度下降。
暴力型的优点在于将文件一次读入到内存,相对来说可以提升一点速度。但缺点很明显,占用内存是整个文件大小,并且读取截取内容之后的行是多余操作,如果这类行数目很多反而拖慢了运行速度。

Local $File, $iPos

$File = FileRead("test.txt")
$iPos = StringInStr($File, "开始行")
$File = StringTrimLeft($File, $iPos)
$iPos = StringInStr($File, "结束行")
If $iPos <> 0 Then
        $iPos += StringLen("结束行")
        StringLeft($File, $iPos)
EndIf
FileWrite("test.txt", $File)

thackit 发表于 2009-4-12 16:56:42

还好文件最多一两百K,内存占用还能接受。多谢以上各位帮忙。

liongodmien 发表于 2009-4-12 21:04:11

参考:
$File = 文件内容 $a = 需要保留字符段前面的部分字符 $b = 需要保留字符段后面的部分字符
$STR = StringRegExp($File, '(?<=' & $a & ').+(?=' & $b, 2, 1)
页: [1]
查看完整版本: 请教提取文本文件中某段内容的最快方法?