| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 4294 人关注过本帖, 1 人收藏
标题:求教如何高效有效的处理巨大TXT文件?
只看楼主 加入收藏
ictest
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:333
专家分:114
注 册:2010-2-17
结帖率:70%
收藏(1)
已结贴  问题点数:50 回复次数:17 
求教如何高效有效的处理巨大TXT文件?
程序我已经写好,可以使用,能达到预期效果。该程序对TXT的文件处理方式是逐行读取和判断,然后删除,写入。
目前我遇到的问题是,需要处理的TXT文件太大,几十兆至上百兆,内容十几万行乃至几十或上百万行,这样处理的时间太长,至少需要7~8分钟乃至几十分钟,甚至报错死机,
求教各位前辈如何改写我的程序,或者说使用别的方法能够秒处理。听说有读入内存处理或者说数组处理的方法,但这些方法我都不会,只能死板的使用逐行读取、判断、删除、写入、循环的笨方法,这就导致程序效率极其低下。
求各位前辈帮助改写程序!这里深深拜谢了!
三个附件分别为程序源码、用于验证的大的数据文件、用于验证的小的数据文件。
数据处理.rar (8.4 KB)
数据文件-大.rar (2.47 MB)
数据文件-小.rar (387.02 KB)
搜索更多相关主题的帖子: 如何 
2017-05-09 08:54
ictest
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:333
专家分:114
注 册:2010-2-17
收藏
得分:0 
还请路过的前辈和各位大神帮忙解决。
2017-05-09 17:00
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4947
专家分:30084
注 册:2008-10-15
收藏
得分:5 
看了一下你的程序,怎么说呢,
首先,VB 处理这文件就是慢。
其次,你代码未经任何优化,只是堆在一起。
例如,
1、每一个文件,需要打开4次,并且每次都需要完全读完。
   你第一次使用二进制读取,并且分好了行的数据,为啥不留给后面几次使用呢,而当作一次性数据就丢弃了。  
   在我电脑上,第一次打开大文件 ,需要 10 秒钟。你算一下,4次打开这就浪费了多少时间?

2、查找    Device#: 18 时,会遍类每一个字符,找到后也不退出,而是继续查找。
   你完全可以 查找该行是否存在 Device#: 这个关键字就可以了,使用 instr 函数。

3、很多地方直接使用控件值
   val(text1.text) 这使用,比
   T1txt=val(text1.text)
然后使用 T1txt 要慢非常多。建议你所有的控件关键属性,都保存到变量里,全部使用变量来完成操作,控件需要显示时,临时显示变量的内容。

4、代码重复太多
If Dir(Dir1.Path & "\" & Label71.Caption & ".bak") = "" Then
这个为条件判断,判断后文件不存在,就复制备份。然后后面的操作都是一样的,就不需要再分为二个段。而到备份完成后就结束判断。

5、split 函数 比 instr 函数要慢。
判断时,先使用 instr 函数,判断是否存在指定的数据,存在再调用 split 函数,而不应该一股垴的都调用 split 函数。

----------------
对文件操作最快的方式就是 内存映射文件,这个你在另一个贴子里贴了,我对这一块也不熟,就不多说了。

好吧,才看半个函数,没时间了。
自己多研究一下。


授人于鱼,不如授人于渔
早已停用QQ了
2017-05-09 18:33
ictest
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:333
专家分:114
注 册:2010-2-17
收藏
得分:0 
一针见血啊!一个“堆”字点出了我写程序的真实水准。拜服。
您提出的意见我真心接受,我叫针对您提出的问题对我的程序进行优化。谢谢您!
由于数据文件文件实在是真心的大,处理时间估计最多能省掉1/3,还能快点么?因为处理那个大的数据文件需要45分钟左右,优化后还是需要半个小时。或者还有更快的方法?
内存映射文件方式重来没有见识和使用过,另一个帖子还请前辈回复一下,教教我使用。
2017-05-09 20:07
ictest
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:333
专家分:114
注 册:2010-2-17
收藏
得分:0 
一针见血啊!一个“堆”字点出了我写程序的真实水准。拜服。
您提出的意见我真心接受,我叫针对您提出的问题对我的程序进行优化。谢谢您!
由于数据文件文件实在是真心的大,处理时间估计最多能省掉1/3,还能快点么?因为处理那个大的数据文件需要45分钟左右,优化后还是需要半个小时。或者还有更快的方法?
内存映射文件方式重来没有见识和使用过,另一个帖子还请前辈回复一下,教教我使用。
2017-05-09 21:10
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4947
专家分:30084
注 册:2008-10-15
收藏
得分:5 
没看懂你的后面 的统计函数。
前面一部分:
程序代码:
Option Explicit

Public Const DeviceStr = "Device#:"
Public Const DeviceStrTitle = " Number     Site  Result   Test Name                 Pin   Channel   Low            Measured       High           Force          Loc"

Public Const SiteBin = " Site    Sort     Bin"
Public Const Sitetests = " Site Failed tests/Executed tests"

Public Const Device_F = "========================================================================="
Public Const site_F = "------------------------------------"


Public Type DeviceType
    Device          As String       'Device
    DeviceS         As Long         '所处行数
    SiteBinS        As Long
    SitetestsS      As Long
End Type



Public Type FileTYpe
    
    '文件属性
    Name        As String
    Path        As String
    PathName    As String
    Size        As Long
    DateTime    As String

    data()          As String   '文件数据
    Datacount       As Long     '总行数
    
    '文件头,具体名字,参考 FileT 数组定义
    FileT(9)        As String

    '数据组数
    Count           As Long
    '数据
    Device()          As DeviceType
End Type


Public PF As FileTYpe
Public FileT(9) As String



Public Sub 文件解析()           '不传参数,填充全局变量 PF
'不负责备份文件

Dim i As Long, j As Long
Dim k As Long, tmp1 As String

Dim fr As Long

fr = FreeFile

With PF
    .PathName = .Path & "\" & .Name               '文件名及全路径
    .DateTime = FileDateTime(.PathName)         '文件日期
    .Size = FileLen(.PathName)      '文件大小
    
    Open .PathName For Input As #fr                   '打开文件
        tmp1 = Input(LOF(fr), #fr)
        '.data = Split((Input(LOF(2), #2)), vbCrLf)       '一次性读入内存
        .data = Split(tmp1, vbCrLf)
    Close #fr
    .Datacount = UBound(.data)
    tmp1 = ""                               '清掉内存
    
.Count = 0

For j = 0 To UBound(.data)

    For i = 0 To 8
        k = InStr(1, .data(j), FileT(i))
        If k > 0 Then
            tmp1 = Trim(Mid(.data(j), k + Len(FileT(i)) + 1))
            .FileT(i) = tmp1
            Exit For        '本次循环处理完毕
        End If
    Next i
    
    If InStr(1, .data(j), FileT(9)) > 0 Then  'Site Number 数据在下一行
        tmp1 = Trim(.data(j + 1))
        .FileT(9) = tmp1
    End If
    
    If InStr(1, .data(j), DeviceStr) > 0 Then        'Device#:
        .Count = .Count + 1
    End If
   
Next j

i = 0
ReDim .Device(.Count)
For j = 0 To UBound(.data)
    k = InStr(1, .data(j), DeviceStr)
    If k > 0 Then        'Device#:
        i = i + 1
        .Device(i).DeviceS = j
        .Device(i).Device = Trim(Mid(.data(j), k + Len(DeviceStr)))
    End If
    
    k = InStr(1, .data(j), SiteBin)
    If k > 0 Then
        .Device(i).SiteBinS = j + 2
    End If
    
    k = InStr(1, .data(j), Sitetests)
    If k > 0 Then
        .Device(i).SitetestsS = j + 2
    End If
Next j
End With

End Sub


授人于鱼,不如授人于渔
早已停用QQ了
2017-05-09 23:20
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4947
专家分:30084
注 册:2008-10-15
收藏
得分:5 
程序代码:
Label11.Caption = "正在处理,请等候........."

PF.Name = File1.Filename
PF.Path = Dir1.Path
PF.PathName = PF.Path & "\" & PF.Name                  '文件名及全路径

If Dir(PF.PathName & ".bak") = "" Then
    BakYesNo = True         '备份文件不存在
    FileCopy PF.PathName, PF.PathName & ".bak"
Else
    BakYesNo = False        '备份文件存在
    
End If
   

Call 文件解析

    Label71.Caption = PF.Name          '显示文件名

    Label2.Caption = PF.PathName                '文件名及全路径
    Label8.Caption = PF.DateTime          '文件日期
    Label10.Caption = Format(PF.Size / 1024, "0.00") & " K 字节"       '文件大小
    Label6.Caption = "共有 " & PF.Datacount + 1 & " 行数据。"             '显示行数

For i = 0 To 9
    Label53(i).Caption = PF.FileT(i)
Next i


Label4.Caption = "共有 " & PF.Count & " 个数据"


从这个向后,不知道你的统计算法。无法继续。
备份文件的信息还没处理

整个文件都被拆了,然后分门别类的进行起始位置统计了。放在 PF 这个结构体内。


授人于鱼,不如授人于渔
早已停用QQ了
2017-05-09 23:22
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4947
专家分:30084
注 册:2008-10-15
收藏
得分:5 
程序代码:
'文件头,对应的是 Label53 个各字段的顺序
FileT(0) = "Prog Name:"
FileT(1) = "Job Name:"
FileT(2) = "Lot:"
FileT(3) = "Operator:"
FileT(4) = "Test Mode:"
FileT(5) = "Node Name:"
FileT(6) = "Part Type:"
FileT(7) = "Channel map:"
FileT(8) = "Environment:"
FileT(9) = "Site Number:"


label53-62,改成控件数组。

授人于鱼,不如授人于渔
早已停用QQ了
2017-05-09 23:24
ictest
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:333
专家分:114
注 册:2010-2-17
收藏
得分:0 
关于我程序后面的统计,如下的一张图我想能够说明了:
图片附件: 游客没有浏览图片的权限,请 登录注册
图片说明.rar (163.17 KB)


[此贴子已经被作者于2017-5-10 12:59编辑过]

2017-05-10 12:57
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4947
专家分:30084
注 册:2008-10-15
收藏
得分:5 
程序代码:
Option Explicit

Public Const DeviceStr = "Device#:"
Public Const DeviceStrTitle = " Number     Site  Result   Test Name                 Pin   Channel   Low            Measured       High           Force          Loc"

Public Const SiteBin = " Site    Sort     Bin"
Public Const Sitetests = " Site Failed tests/Executed tests"

Public Const Device_F = "========================================================================="
Public Const site_F = "------------------------------------"

Public Enum PassFaiL
    PASS = 1
    FAIL = 2
End Enum

Public Enum 清显示范围
    全部 = 0
    分析 = 1
    删除 = 2
End Enum



Public Type DeviceType
    Device          As String       'Device
    DeviceS         As Long         '所处行数
    Sitetests       As Long
    SitetestsS      As Long
    SiteBin         As Long
    SiteBinS        As Long
    PF              As PassFaiL     '这段是 pass 还是 Fail
End Type


Public Type FileTYpe
    
    '文件属性
    Name        As String
    Path        As String
    PathName    As String
    Size        As Long
    DateTime    As String

    data()          As String   '文件数据
    Datacount       As Long     '总行数
    
    '文件头,具体名字,参考 FileT 数组定义
    FileT(9)        As String

    '数据组数
    Count           As Long
    '数据
    Device()        As DeviceType
    PASS            As Long      '统计
    FAIL            As Long
End Type


Public PF As FileTYpe
Public FileT(9) As String



Public Sub 文件解析()           '不传参数,填充全局变量 PF
'不负责备份文件

Dim i As Long, j As Long
Dim k As Long, tmp1 As String

Dim fr As Long

fr = FreeFile

With PF

    .Datacount = 0
    .Count = 0
    .FAIL = 0
    .PASS = 0

    .PathName = .Path & "\" & .Name               '文件名及全路径
    .DateTime = FileDateTime(.PathName)         '文件日期
    .Size = FileLen(.PathName)      '文件大小
    
    '预处理完成
    
    Call Form1.进度条(0.05, "准备打开文件..")
    
    Open .PathName For Input As #fr                   '打开文件
        tmp1 = Input(LOF(fr), #fr)                       '一次性读入内存
        
        Call Form1.进度条(0.1, "打开文件完成,准备分行")
        
        .data = Split(tmp1, vbCrLf)
        
        Call Form1.进度条(0.15, "分行完成,开始分析")
    Close #fr
    .Datacount = UBound(.data)
    tmp1 = ""                               '清掉内存
    
.Count = 0

    Call Form1.显示日志("正在分析步骤一..")
For j = 0 To .Datacount
    
    If j Mod 10 = 0 Then        '每处理10个变化一次进度条
        Call Form1.进度条(0.15 + j / .Datacount * 0.43)
    End If

    For i = 0 To 8
        k = InStr(1, .data(j), FileT(i))
        If k > 0 Then
            tmp1 = Trim(Mid(.data(j), k + Len(FileT(i)) + 1))
            .FileT(i) = tmp1
            Exit For        '本次循环处理完毕
        End If
    Next i
    
    If InStr(1, .data(j), FileT(9)) > 0 Then  'Site Number 数据在下一行
        tmp1 = Trim(.data(j + 1))
        .FileT(9) = tmp1
    End If
    
    If InStr(1, .data(j), DeviceStr) > 0 Then        'Device#:
        .Count = .Count + 1
    End If
   
Next j


    Call Form1.显示日志("正在分析步骤二..")

i = 0
ReDim .Device(.Count)
For j = 0 To .Datacount

    If j Mod 10 = 0 Then        '每处理10个变化一次进度条
        Call Form1.进度条(0.58 + j / .Datacount * 0.4)
    End If

    k = InStr(1, .data(j), DeviceStr)
    If k > 0 Then        'Device#:
        i = i + 1
        .Device(i).DeviceS = j
        .Device(i).Device = Trim(Mid(.data(j), k + Len(DeviceStr)))
    End If
    
    k = InStr(1, .data(j), SiteBin)
    If k > 0 Then
        .Device(i).SiteBinS = j + 2
        '解析SiteBin
        .Device(i).SiteBin = Mid(.data(j + 2), 24, 8)
    End If
    
    k = InStr(1, .data(j), Sitetests)
    If k > 0 Then
        .Device(i).SitetestsS = j + 2
        '解析Sitetest
        .Device(i).SiteBin = Val(Mid(.data(j + 2), 14, 8))
    End If
Next j

    Call Form1.显示日志("正在分析步骤三..")

'解析完需要统计
For j = 1 To .Count

    If j Mod 10 = 0 Then        '每处理10个变化一次进度条
        Call Form1.进度条(0.98 + j / .Count * 0.02)
    End If


    If .Device(j).SiteBin = 1 And .Device(j).Sitetests = 0 Then
        .Device(j).PF = PASS
        .PASS = .PASS + 1
    Else
        .Device(j).PF = FAIL
        .FAIL = .FAIL + 1
    End If
Next j


End With
    
    Call Form1.显示日志("文件解析完成")

End Sub


Public Sub 保存Pass()

'Dim fn As String
Dim fr As Long
fr = FreeFile

Dim i As Long
Dim j As Long

Call Form1.进度条(0.01, "开始保存Pass数据")
Open (PF.Path & "\" & "Tmp-Pass.txt") For Output As fr

Call Form1.进度条(0.01, "写入文件头")
'先写入头
For j = 0 To PF.Device(1).DeviceS - 1
    Print #fr, PF.data(j)
Next j

Call Form1.进度条(0.05, "开始写入数据")
'再写入每节
For i = 1 To PF.Count
    
Call Form1.进度条(0.05 + i / PF.Count * 0.9)

    If PF.Device(i).PF = PASS Then
        For j = PF.Device(i).DeviceS To PF.Device(i).SiteBinS
            Print #fr, PF.data(j)
        Next j
        '写完后,写入分隔符
        Print #fr, Device_F
    End If
Next i
Close #fr
            
If Dir(PF.PathName & "-Pass.txt") <> "" Then
    Kill (PF.PathName & "-Pass.txt")
End If

Name (PF.Path & "\" & "Tmp-Pass.txt") As (PF.PathName & "-Pass.txt")

Call Form1.进度条(1, "保存数据完成!")

End Sub

授人于鱼,不如授人于渔
早已停用QQ了
2017-05-10 20:42
快速回复:求教如何高效有效的处理巨大TXT文件?
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.026013 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved