____________________________________________致歉声明:以前在这里回答过关于按行读文本文件的问题,
我可能有误导的言论,在此向被我误导的朋友致歉
____________________________________________我一直想当然的认为分行读效率不如整个读到一个字符串然后再按换行符split到数组效率高但是在.NET区被高手指正我的说法是错误的
http://community.csdn.net/Expert/topic/4821/4821206.xml
我不知道在VB6中是不是与.NET中一样,我这里没有VB6环境,希望有VB6的朋友帮我测试一下,以解除我的疑惑,先谢了~

解决方案 »

  1.   

    不用测试,VB6里分行读会慢的,.NET里面是用流来处理的,分不分速度应该差别不大。而VB6不是,分行的循环导致对文件的多次访问,非常典型的示例是读一个数据文件,一个记录一个记录的读非常漫长,一次性读入BYTE数组的速度和API读相等,都快很多。这个我早就测试过了。是一个SAMSUNG DIY VB DEMO程序,几年前写的,当时要处理手机的几十兆BIN文件。以上就是我的论调了。。相信大家测试以后能得到相同结论。。
      

  2.   

    说明一下,我个人认为VB6读文件是你要多少它从磁盘上读多少,OPEN语句之是对文件句柄进行了打开,实际读还是用GET、INPUT等。所以上面我说“分行的循环导致对文件的多次访问”指对磁盘的多次访问.NET我是在3年多前开始学的,到现在还是超级菜,别看时间长,呵呵毕竟我是个业余的,编程是业余爱好,所以.NET涉猎不是很深(其实VB6也不怎么样)不对.NET的流操作进行说明了,以免更多失言。
      

  3.   

    处理文本文件我都用FSO对象,不用VB传统的文件处理方法。
    在FSO对象里READALL是最慢的其次是READLINE,最好的是先确定文件长度然后READ(SIZE),但考虑到文件太大会造成内存不足,所以READLINE应该是最优的。
      

  4.   

    本来我与viena 的观点基本一致,但从下面测试代码看来.NET区高手所言非虚Option ExplicitFunction timereadfiletoarray(ByVal index As Integer, ByVal filename As String, ByRef x() As String) As String
    Dim b() As Byte, ttime As Single, temp As String, i As Long, fso As New FileSystemObject, ts As TextStream
    ttime = Timer
    Select Case indexCase 1
    Open filename For Binary As #1
    ReDim b(LOF(1))
    Get #1, , b
    Close #1
    x = Split(StrConv(b, vbUnicode), vbCrLf)
    Case 2
    Open filename For Binary As #1
    temp = Space(LOF(1))
    Get #1, , temp
    Close #1
    x = Split(temp, vbCrLf)Case 3
    Open filename For Input As #1
    temp = StrConv(InputB(LOF(1), 1), vbUnicode)
    Close #1
    x = Split(temp, vbCrLf)Case 4
    ReDim x(0)
    Open filename For Input As #1
    Do While Not EOF(1)
    Line Input #1, x(i)
    i = i + 1
    ReDim Preserve x(i)
    Loop
    Close #1Case 5
    Set ts = fso.OpenTextFile(filename)
    temp = ts.ReadAll
    x = Split(temp, vbCrLf)
    ts.CloseCase 6
    ReDim x(0)
    Set ts = fso.OpenTextFile(filename)
    Do While Not ts.AtEndOfStream
    x(i) = ts.ReadLine
    i = i + 1
    ReDim Preserve x(i)
    Loop
    ts.Close
    End Selecttimereadfiletoarray = "文件" & filename & "大小" & FileLen(filename) & "bytes,使用方法" & index & "赋值到数组X()用时" & Format(Timer - ttime, "0.0000") & "秒!"End FunctionPrivate Sub Command1_Click()
    Text1.Text = ""
    Dim i As Integer, j As Integer, x() As String
    For j = 1 To 3
    For i = 1 To 6
    Debug.Print timereadfiletoarray(1, "e:\" & j & ".txt", x)
    Next
    Debug.Print
    Next
    End Sub测试结果如下:'文件e:\1.txt大小566890bytes,使用方法1赋值到数组X()用时0.0781秒!
    '文件e:\1.txt大小566890bytes,使用方法2赋值到数组X()用时0.0469秒!
    '文件e:\1.txt大小566890bytes,使用方法3赋值到数组X()用时0.0469秒!
    '文件e:\1.txt大小566890bytes,使用方法4赋值到数组X()用时0.0625秒!
    '文件e:\1.txt大小566890bytes,使用方法5赋值到数组X()用时0.2656秒!
    '文件e:\1.txt大小566890bytes,使用方法6赋值到数组X()用时0.0781秒!'文件e:\2.txt大小4348798bytes,使用方法1赋值到数组X()用时1.4688秒!
    '文件e:\2.txt大小4348798bytes,使用方法2赋值到数组X()用时1.6719秒!
    '文件e:\2.txt大小4348798bytes,使用方法3赋值到数组X()用时1.7656秒!
    '文件e:\2.txt大小4348798bytes,使用方法4赋值到数组X()用时2.8125秒!
    '文件e:\2.txt大小4348798bytes,使用方法5赋值到数组X()用时1.8594秒!
    ''文件e:\2.txt大小4348798bytes,使用方法6赋值到数组X()用时2.8906秒!
    '
    '文件e:\3.txt大小43488140bytes,使用方法1赋值到数组X()用时237.9531秒!
    '文件e:\3.txt大小43488140bytes,使用方法2赋值到数组X()用时311.9688秒!
    '文件e:\3.txt大小43488140bytes,使用方法3赋值到数组X()用时302.0469秒!
    '文件e:\3.txt大小43488140bytes,使用方法4赋值到数组X()用时31.4531秒!
    '文件e:\3.txt大小43488140bytes,使用方法5赋值到数组X()用时189.0313秒!
    '文件e:\3.txt大小43488140bytes,使用方法6赋值到数组X()用时47.1719秒!
      

  5.   

    从以上测试结果不难看出:各种方法的速度与文件大小密切相关。文件越大,按行读越快。小文件在VB6按行读没有任何优势。至于使用FSO,ts.ReadAll确实很慢。
      

  6.   

    用split的方案,速度慢得出乎想象了,会不会耗尽物理内存了?
    超级大的文件,在耗尽内存的情况下(用虚拟内存了)速度自然超级慢。(猜测:即使内存没有耗尽,如果受到VB规格限制(堆、栈都有大小限制),也不得不使用虚拟内存。)
    如果大文件耗尽物理内存了,那么这种比较就不合理了。我本人还是比较倾向于一次读入然后再split的。
      

  7.   

    上面的代码我不知道是怎么测试的,我提供一个代码,本代码在XP,VB6SP6下测试
    我电脑上测试结果如下(已经关闭诺盾文件监视):
    行写2字符5W行               47.0000
    行写200字符5W行             390.0000
    字写2字符5W行               31.0000
    字写200字符5W行             735.0000这里读的文件大小是相同的,用行输出得到的文本:L10000S258L---9,100,000字节
    行读200字符5W行             437.0000
    字读200字符5W行             141.0000这里读的文件大小是相同的,用字节输出得到的文本:L10000S258B.txt---36,400,004字节
    行读200字符5W行             437.0000
    字读200字符5W行             485.0000提供我的测试代码:(写的比较匆忙,将就看吧,不过应该不是很难懂)'测试请建立一个窗体,需要一个按钮,名称默认
    Option Explicit
    Private Declare Function GetTickCount Lib "kernel32" () As Long
    Private Const mStrI = "12"
    Private Const mStrL = "123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890"
    Dim mByte() As Long
    Dim tTime As Long
    Dim mStr As String
    Private Sub Command1_Click()
    Dim i As Long
    Debug.Print
    killfile
    '以下写的文件大小不同
    tTime = GetTickCount
    '行写2字符1W行
    Open "c:\L10000S2L.txt" For Output As #1
        For i = 0 To 49999
            Print #1, mStrI
        Next
    Close #1
    Debug.Print "行写2字符5W行 ", Format(GetTickCount - tTime, "0.0000")
    DoEvents
    tTime = GetTickCount
    '行写200字符1W行
    Open "c:\L10000S258L.txt" For Output As #1
        For i = 0 To 49999
            Print #1, mStrL
        Next
    Close #1
    Debug.Print "行写200字符5W行", Format(GetTickCount - tTime, "0.0000")'读
    Open "c:\L10000S2L.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    DoEvents
    tTime = GetTickCount
    '字写2字符1W行
    Open "c:\L10000S2B.txt" For Binary As #1
        Put #1, , mByte
    Close #1
    Debug.Print "字写2字符5W行 ", Format(GetTickCount - tTime, "0.0000")'读
    Open "c:\L10000S258L.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    DoEvents
    tTime = GetTickCount
    '字写200字符1W行
    Open "c:\L10000S258B.txt" For Binary As #1
            Put #1, , mByte
    Close #1Debug.Print "字写200字符5W行", Format(GetTickCount - tTime, "0.0000")Debug.Print'这里读的文件大小是相同的,用行输出得到的文本
    Debug.Print "这里读的文件大小是相同的,用行输出得到的文本:L10000S258L---9,100,000字节"
    DoEvents
    tTime = GetTickCount
    '行读200字符1W
    Open "c:\L10000S258L.txt" For Input As #1
        For i = 0 To 49999
            Line Input #1, mStr
        Next
    Close #1Debug.Print "行读200字符5W行", Format(GetTickCount - tTime, "0.0000")
    DoEvents
    tTime = GetTickCount
    '字读200字符1W行
    Open "c:\L10000S258L.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    Debug.Print "字读200字符5W行", Format(GetTickCount - tTime, "0.0000")Debug.Print
    '这里读的文件大小是相同的,用字节输出得到的文本
    Debug.Print "这里读的文件大小是相同的,用字节输出得到的文本:L10000S258B.txt---36,400,004字节"
    DoEvents
    tTime = GetTickCount
    '行读200字符1W
    Open "c:\L10000S258B.txt" For Input As #1
        For i = 0 To 49999
            Line Input #1, mStr
        Next
    Close #1Debug.Print "行读200字符5W行", Format(GetTickCount - tTime, "0.0000")
    DoEvents
    tTime = GetTickCount
    '字读200字符1W行
    Open "c:\L10000S258B.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    Debug.Print "字读200字符5W行", Format(GetTickCount - tTime, "0.0000")
    killfile
    End SubSub killfile()
    On Error Resume Next
    Kill "c:\L10000S2L.txt"
    Kill "c:\L10000S258L.txt"
    Kill "c:\L10000S2B.txt"
    Kill "c:\L10000S258B.txt"
    End Sub
      

  8.   

    上面代码测试时有问题了:怎么和我上面提出的观点就不一样呢:
    这里读的文件大小是相同的,用字节输出得到的文本:L10000S258B.txt---36,400,004字节
    行读200字符5W行             437.0000
    字读200字符5W行             485.0000
    我的观点是:字读比行读快。
    还是坚持以上观点,原因:
    上面的代码里面,在字读的时候REDIM,行读就没有,这一RE可弄没了不少时间
    刚才让行读也RE了一下,不过感觉不对头,它们都RE但是RE的不太一样,于是都不RE,得到如下结果:
    行写2字符5W行               31.0000
    行写200字符5W行             391.0000
    字写2字符5W行               31.0000
    字写200字符5W行             672.0000这里读的文件大小是相同的,用行输出得到的文本:L10000S258L---9,100,000字节
    行读200字符5W行             500.0000
    字读200字符5W行             47.0000这里读的文件大小是相同的,用字节输出得到的文本:L10000S258B.txt---36,400,004字节
    行读200字符5W行             422.0000
    字读200字符5W行             203.0000我还是坚持我的观点:字读快,行读慢,咱就看看只让他们读,别的操作没有的情况下到底如何了
    以下是测试代码:测试时在窗体上添加一个名为COMMAND1的按钮
    Option Explicit
    Private Declare Function GetTickCount Lib "kernel32" () As Long
    Private Const mStrI = "12"
    Private Const mStrL = "123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890123456890"
    Dim mByte() As Long
    Dim tTime As Long
    Dim mStr(49999) As String
    Private Sub Command1_Click()
    Dim i As Long
    Debug.Print
    killfile
    '以下写的文件大小不同
    tTime = GetTickCount
    '行写2字符1W行
    Open "c:\L10000S2L.txt" For Output As #1
        For i = 0 To 49999
            Print #1, mStrI
        Next
    Close #1
    Debug.Print "行写2字符5W行 ", Format(GetTickCount - tTime, "0.0000")
    DoEvents
    tTime = GetTickCount
    '行写200字符1W行
    Open "c:\L10000S258L.txt" For Output As #1
        For i = 0 To 49999
            Print #1, mStrL
        Next
    Close #1
    Debug.Print "行写200字符5W行", Format(GetTickCount - tTime, "0.0000")'读
    Open "c:\L10000S2L.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    DoEvents
    tTime = GetTickCount
    '字写2字符1W行
    Open "c:\L10000S2B.txt" For Binary As #1
        Put #1, , mByte
    Close #1
    Debug.Print "字写2字符5W行 ", Format(GetTickCount - tTime, "0.0000")'读
    Open "c:\L10000S258L.txt" For Binary As #1
        ReDim mByte(LOF(1))
        Get #1, , mByte
    Close #1
    DoEvents
    tTime = GetTickCount
    '字写200字符1W行
    Open "c:\L10000S258B.txt" For Binary As #1
            Put #1, , mByte
    Close #1Debug.Print "字写200字符5W行", Format(GetTickCount - tTime, "0.0000")Debug.Print'这里读的文件大小是相同的,用行输出得到的文本
    Debug.Print "这里读的文件大小是相同的,用行输出得到的文本:L10000S258L---9,100,000字节"
    DoEvents
    tTime = GetTickCount
    '行读200字符1W
    Open "c:\L10000S258L.txt" For Input As #1
        For i = 0 To 49999
            Line Input #1, mStr(i)
        Next
    Close #1Debug.Print "行读200字符5W行", Format(GetTickCount - tTime, "0.0000")'咱先获取文件字节数REDIM
    Open "c:\L10000S258L.txt" For Binary As #1
        ReDim mByte(LOF(1))
    Close #1
    DoEvents
    tTime = GetTickCount
    '字读200字符1W行Open "c:\L10000S258L.txt" For Binary As #1
        Get #1, , mByte
    Close #1
    Debug.Print "字读200字符5W行", Format(GetTickCount - tTime, "0.0000")Debug.Print
    '这里读的文件大小是相同的,用字节输出得到的文本
    Debug.Print "这里读的文件大小是相同的,用字节输出得到的文本:L10000S258B.txt---36,400,004字节"
    DoEvents
    tTime = GetTickCount
    '行读200字符1W
    Open "c:\L10000S258B.txt" For Input As #1
        For i = 0 To 49999
            Line Input #1, mStr(i)
        Next
    Close #1Debug.Print "行读200字符5W行", Format(GetTickCount - tTime, "0.0000")'咱先获取文件字节数REDIM
    Open "c:\L10000S258B.txt" For Binary As #1
        ReDim mByte(LOF(1))
    Close #1
    DoEvents
    tTime = GetTickCount
    '字读200字符1W行
    Open "c:\L10000S258B.txt" For Binary As #1
        Get #1, , mByte
    Close #1
    Debug.Print "字读200字符5W行", Format(GetTickCount - tTime, "0.0000")
    killfile
    End SubSub killfile()
    On Error Resume Next
    Kill "c:\L10000S2L.txt"
    Kill "c:\L10000S258L.txt"
    Kill "c:\L10000S2B.txt"
    Kill "c:\L10000S258B.txt"
    End Sub
      

  9.   

    从上面可以清晰的得到结论:
    1、REDIM占用了大量时间(如第一个代码)
    2、在处理行写得到的文件(纯文本文件)时,字节读比行读优势大非常多,8倍
    3、在处理字写得到的文件(2进制文件)时,字读比行读优势小一些,2倍
    4、在处理2进制文件是,行读速度提高了一些
    行读200字符5W行             500.0000
    行读200字符5W行             422.0000
    但是需要注意的是,2进制文件一般无法用行读处理,我这里的代码是将文本读回来用2进制去写的,其他2进制文件人家可不会给你分行,行读不可行的。
    结束;还是坚持我的结论,VB6中字读比行读快
      

  10.   

    一次全读比分行读肯定快,这毫无疑问的;
    但还是在同一个数量级,差别不大;但split分行耗费了大量的时间,这与CPU的速度有一定关系;基本可以得出结论,如果确实需要得到分行的字符串;
    分行读是最好的方法;
    我以前认为分行读无法充分利用缓冲区的观点是错误的;如果只是要得到行数另当别论如果只需要分析不需要得到字符串,还有一个方法,比较麻烦
    就是二进制读到Byte数组,遍历数组直接分析字符编码
    比如数组元素为13,就知道是回车符
      

  11.   

    northwolves(狼行天下)的测试ReDim Preserve x(i)
    也是比较花时间的,
    去掉ReDim的话,分行读应该是最快的
    而且不需要占额外的CPU资源如果需求是只需要得到每一行的字符串,
    应该是首选
      

  12.   

    恩恩,放眼望去皆高手啊,看来没我说话的余地了,学习ING?我看这个问题也讨论的差不多了,关键还是看需求了,其实话说回来,如果单纯讨论读的时候谁快的问题我第一个支持BYTE,因为做过多次测试,结果基本相同:使用API读的速度和BYTE基本是差不多的,但是呢,如果你要读文本行数据,进行字符处理,那2进制读回来还是要处理的,这个处理会用很多时间,上面代码里我把REDIM拿到读的外面去了,关键是测试读的速度,但是实际呢,REDIM是必须的,也就是说,什么时候处理REDIM只是看你的策略了,让用户感觉不到慢就可以了吧
      

  13.   

    难道OS会傻到有实际内存可用,却去硬盘上折腾吗?
    答案是会!
    除非你禁止使用页面文件。我的禁止了。。
    既然说到这里了,就说说我电脑一般配置,得到那些结论。。
    双通道DDR400共1GB
    CPU SY D325
    SATA 硬盘
      

  14.   

    viena(维也纳nn [IQ=50,EQ<0]) OK
      

  15.   

    这个问题实际上是文件大小与内存大小的关系问题。当你的文件很大,而你又要一次将文件读入虚拟内存,系统就会将大量内容读入磁盘缓冲区。当你 Split 时,要一点点挪出来,处理后,再一点点存入一处新的磁盘缓冲区,螺蛳壳里作道场,当然慢了。这个问题是秃子头上的虱子,明摆着的。如果你搞过汇编,或者做过“三塔”游戏(锻炼处理器思维的),就应该明白这个道理。所以,任何算法的优劣,要将条件的。就好比课本上酱排序算法,都是讲什么条件下孰优孰劣的。
      

  16.   

    //晕倒,这个帖子咋还没结捏。
    呵呵,我想让更多的人看到你看一下我在题目里提到.NET区那个帖,
    也没有结,呵呵~
      

  17.   

    一次读并且split的效率比按行是要高的多的多。
    当然是文件能全部读到内存不占页面交换文件的情况下。
    实际上, 对于任意文件, 可以10M一读, 然后拆分, 记得处理下2次读之间的衔接处。很多情况下, 在循环行读文件慢的地方, 并不是读的操作,
    而是字符串连接的操作。
    do until eof(1)
    line input #1, temp
    str = str & temp  或者 text1.text=text1.text & temp
    loop慢在了频繁的分配内存上, 尤其是越往后内存数量越大的情况.
    对于跟文件读有关的操作,   绝大部分频颈是在 文件i/o上.
    所以用任何语言处理效率都相差不大, 当然要注意不要频繁分配内存.
      

  18.   

    //慢在了频繁的分配内存上, 尤其是越往后内存数量越大的情况对于楼上所说的情况,我写了个CStringBuffer类,是模仿Java的StringBuffer
    也许实现方法比较笨拙,但也许可以结决大量字符串连接问题
    http://blog.csdn.net/viena/archive/2005/10/10/498612.aspx
      

  19.   

    难得见到vb板块这么热闹,哈哈,UP一下,学习..........
      

  20.   

    谁建议对打字符串用 split 进行分割的?该扁!如果文件大小中等(可以一次载入到物理内存),用下面的 StringReader.cls
    Option Explicit
    Private m_str           As String
    Private m_lLen          As Long
    Private m_lPosition     As Longsub OpenFile(byval FileName)
        一次读入到 m_str
        m_lLen = Len(m_str)
        m_lPosition = 1
    end subFunction ReadLine() As String
        Dim i               As Long
        
        If m_lPosition > m_lLen Then Exit Function
        
        i = InStr(m_lPosition, m_str, vbCrLf)
        If i = 0 Then i = m_lLen + 1
        
        ReadLine = Mid$(m_str, m_lPosition, i - m_lPosition)
        m_lPosition = i + 2
    End Function如果文件更大,需要对用缓存方式对 StringReader 进行改造:
    一、如果文件的行最大字符数容易估计,直接将 Buffer 的大小开成 2 倍以上的行最大字符数,这样 ReadLine 中:只要能找到下个 vbCrLf,就用 Mid 返回结果;否则就将前个 vbCrLf 后免的字符移动到 Buffer 头部,然后读取文件将剩余部分 Buffer 填满。
    二、如果行最大字符数无法确定,只能开一个大小比较合理的 Buffer,在 ReadLine 中只要没有找到 vbCrLf,就将整个 Buffer 拼接到返回字符串后面,继续循环读取整个 Buffer。过于频繁地拼接返回字符串会对性能稍有影响。