当前位置:编程学习 > VB >>

还是随机文件读写问题

问题代码:
Option Explicit
Option Base 1
'Private Type data
'u As String * 1
'v As String * 1
'w As String * 1
'x As String * 1
'y As String * 1
'z As String * 1
'End Type
'Dim d As data, s As data
Private Sub command1_click()
Dim c As Integer, h As Integer
Dim d(24) As String * 3, s(24) As String * 3
Dim e(7) As String * 3
Open "h:\test1.txt" For Random As #1 Len = Len(s(1))
Open "h:\test3.txt" For Random As #2 Len = Len(d(1))
Open "h:\test2.txt" For Random As #3 Len = Len(s(1))
c = 1
For h = 1 To 24
Get #1, c, s(h)
Put #3, c, s(h)
'Get #3, c, e(h)
c = c + 1
Next h
Put #2, 1, s(13)
Debug.Print s(11) ', s.v, s.w, s.x, s.y, s.z
Close #1, #2, #3
End Sub
需要先说明的是txt文件中的数据格式类似:
99 22 83 02 12 21
23 12 04 19 03 44
11 94 38 06 38 84
23 94 75 08 93 24
......
每六个数一行,可以有很多行.问题是这样的,我无论怎么调整,读出的数据从第三行起就变成:1 9  4  3  8 0 6 3  8  8 ...好象是多了两个空格。但是前两行却是正常的,真是奇怪。
我是初学者,请各位高人指点指点,谢谢。 --------------------编程问答--------------------
dim sBuffer as string,sLineBuffer() as string
dim I as long

open "xxx.txt" for binary as #1
    sbuffer=space(lof(1))
    get #1,,sbuffer
close #1

slinebuffer()=split(sbuffer,vbcrlf)

for i=0 to ubound(slinebuffer)
    debug.print slinebuffer(i)
next
--------------------编程问答-------------------- 方法错误!
普通的文本文件不适合以 Random 模式来读取。
你用了 Random模式 ,但你的‘记录长度’是不合理的。

就算你要用这种模式来读取,首先你要明确你的文本文件的行分界符是 vbCrLf 还是 vbLF 。
一般情况下行分界符是 vbCrLf ,这样实际上每行是 20个字符,不是3的整数倍。
读取过程中,就会出现‘错位’的现象。
--------------------编程问答-------------------- 其实你的文本文件以 Input模式打开,用 Line Input 来读取,再用 Split() 把它拆分成单个数据。
相信简单且不易出错。
--------------------编程问答-------------------- 谢谢,不过我忘记说明了,我需要随时定位读取,如果random方式可以,应该是最佳方式。 --------------------编程问答-------------------- 文件只要在50M以内的话,都可以直接读到内存,哈哈 --------------------编程问答-------------------- 但请问读入内存后呢?我是新手,请明示,再次感谢 --------------------编程问答-------------------- 我明白了,你说的读入内存的方法,只要用line input一次读入一个大数组即可,这个方法我也知道,但我觉得这不是一个编程的好习惯,而且此文件容量会不断增加,理论上会超过50M,所以我还是希望用类似random的方法。 --------------------编程问答-------------------- 用 Binary模式 也可以随机读写啊!
要是你的每行数据长度是相同的,用 Binary模式 没问题。


现在主要是你在用 Random模式 读写文件时,你的‘记录长度’错误的。
仔细看一下我 2F 的回复。
--------------------编程问答-------------------- 解决问题要从根本上解决
不要把注意力都放在如何读文件上 
重点是在写文件时选用一个合适的文件模式
--------------------编程问答-------------------- txt文件中的数据格式类似: 
99 22 83 02 12 21 
23 12 04 19 03 44 
11 94 38 06 38 84 
23 94 75 08 93 24 
...... 
每六个数一行,可以有很多行.

//

如果格式固定,那就好说了呀.

按这格式,可以计算出指定行的字节数,公式:

(17+2)*LineIndex+1

比如,要读第3行的数据,则得到第三行的开始字节即:

(17+2)*3+1=58

那么第58字节开始,读17字节,就是第3行的内容了. --------------------编程问答-------------------- 首先感谢10楼的建议.但是我还是要说,这个公式是不是有些问题或者是我的文件有问题,因为我发现文件实际字节数与此公式计算结果十分不同。我测了一下,一行有18字节,两行共有36字节,三行共有55字节,四行共有74字节,五行共有94字节......依次类推,正象Chen8013说的是‘记录长度’错误。但是一次将其读入内存,却不会有什么问题,但这又回到我前面说的,这不是一个最终解决的办法。如果Random模式是个错误,而且又如此没有规律,我如何在Binary模式中随意读出某行某个数据呢(例如,读取第三行第三个数据,即38)?我是个新手,还请各位高人明示。 --------------------编程问答-------------------- 天!这点屁事儿就搞不定吗?我实在看不下去了!


Private Sub Command2_Click()
  Dim tI As Long
  Dim tJ As Long
  For tI = 0 To 3
    For tJ = 0 To 5
      Debug.Print GetValue("Test.txt", tI, tJ);
    Next
    Debug.Print
  Next
End Sub

Function GetValue(ByVal pFileName As String, ByVal pLine As Long, ByVal pRow As Long, Optional pCellSize As Long = 3, Optional ByVal pLineLength As Long = 20, Optional ByVal pStart As Long = 1) As String
  'pFileName    文件名。必要参数。
  'pLine        行号。必要参数。从0开始计算,第一行编号为0。
  'pRow         列号。必要参数。从0开始计算,第一列编号为0。
  'pCellSize    元长。可选参数。单个数值字节长度,默认值为3。
  'pLineLength  行宽。可选参数。默认值为20字节(3×6+2)。
  'pStart       地址偏移。可选参数。默认值为1,从第一字节开始读。
  '返回值       指定行列的数值(2位数值,末尾带空格)
  Dim tOutString As String
  Dim tOutBytes() As Byte
  Dim tOutBytes_Length As Byte
  
  Dim tFileNumber As Integer
  Dim tFileStart As Long
  
  tOutBytes_Length = pCellSize - 1
  
  ReDim tOutBytes(tOutBytes_Length)
  
  tFileNumber = FreeFile
  
  tFileStart = pLine * pLineLength + pRow * pCellSize + pStart
  
  Open pFileName For Binary As #tFileNumber
    Get #tFileNumber, tFileStart, tOutBytes()
  Close tFileNumber
  
  tOutString = StrConv(tOutBytes(), vbUnicode)
  
  GetValue = tOutString
End Function


文件内容:
99 22 83 02 12 21 
23 12 04 19 03 44 
11 94 38 06 38 84 
23 94 75 08 93 24 

打印值:
99 22 83 02 12 21 
23 12 04 19 03 44 
11 94 38 06 38 84 
23 94 75 08 93 24 

后面3个可选参数用默认值就能返回正确结果。

如果你文件格式特别,就指定一个实际的pLineLength值。
DOS和Windows文本是pLineLength=20(默认值就是这样)
对于Linux文本,pLineLength=19。

如果某天设计数据文件这家伙脑袋被门挤到了,要把数值改成3位数、把每行6个数值改成160个数值。
那么你只需要把pLineLength设置为642、pCellSize设置为4就可以正常读。

假如设计数据文件这家伙花痴病犯了,非要在文件头写上100字节的情书。那么你把pStart改成101就可以。

只要你文件保证每行长度一样、每个数据位数一样,那么无论怎么改变。 --------------------编程问答--------------------
如果文件不是特别大,不如事先将文件读入内存,去掉所有空格和回车换行符,然后存入数组。

如果文件特别大,最好转换成二进制文件。
--------------------编程问答--------------------
另外,你的文件格式和读取方法,不符合随机读取的规则。

随机读的一个“分组”,是一个记录,也就是你所说的一行。另外,你的读法,要求每个记录的长度是相同的。但是,你的一行中,有 vbCrLf(2 字节),也有 vbLf(1 字节)。

如果你还是坚持现有的随机读方式的话,就至少应该去掉所有的回车换行符,也可以去掉所有的空格。

当然,你目前改动最少的方法,就是将所有换行符统一。 --------------------编程问答--------------------
引用 11 楼 chyp1 的回复:
首先感谢10楼的建议.但是我还是要说,这个公式是不是有些问题或者是我的文件有问题,因为我发现文件实际字节数与此公式计算结果十分不同。我测了一下,一行有18字节,两行共有36字节,三行共有55字节,四行共有74字节,五行共有94字节......依次类推,正象Chen8013说的是‘记录长度’错误。但是一次将其读入内存,却不会有什么问题,但这又回到我前面说的,这不是一个最终解决的办法。如果Random?-

汗,我那只是一个例子,具体的字节数这些你自己用UE以十六进制打开分析一下嘛!!人不能懒到这程度.... --------------------编程问答--------------------
引用 14 楼 of123 的回复:
..........但是,你的一行中,有 vbCrLf(2 字节),也有 vbLf(1 字节)。

 ............

按楼主 11F 的说法,应该有这个问题。
--------------------编程问答-------------------- 小仙妹回复真有意思~~~~~~~



方法不错,顶…………
--------------------编程问答--------------------
引用 15 楼 myjian 的回复:
引用 11 楼 chyp1 的回复:
首先感谢10楼的建议.但是我还是要说,这个公式是不是有些问题或者是我的文件有问题,因为我发现文件实际字节数与此公式计算结果十分不同。我测了一下,一行有18字节,两行共有36字节,三行共有55字节,四行共有74字节,五行共有94字节......依次类推,正象Chen8013说的是‘记录长度’错误。但是一次将其读入内存,却不会有什么问题,但这又回到我前面说的,这不是一个最终解决的办法。如果Random?-

汗,我那只是一个例子,具体的字节数这些你自己用UE以十六进制打开分析一下嘛!!人不能懒到这程度....


他如果会UE分析文件,就不会在这种问题上纠缠不清了。看起来很是着急啊……

不过话又说回来,谁都是从这个时候过来的。我上初中时候刚学BASIC,也是先接触随机文件。因为当时觉得二进制文件很“可怕”。现在觉得二进制是最简单、最直接的办法。

引用 17 楼 chen8013 的回复:
小仙妹回复真有意思~~~~~~~
方法不错,顶…………


从本质上看来说:他要求根据行、列取固定位数据的需求是一种三维定位式的读文件函数。
第一维是数据单元;第二维是列;第三维是行。
只要是这种固定行列、固定数据位数的文本格式数据,不同的行列、起始位置仅仅是几个参数的区别。这个函数都可以通用。

我为了图省事,省略了一个可选参数,就是pLoadLength。严格来说,还应该有一个pLoadLength参数设置在定位之后读取多少个字节。我在上述函数里简化成pCellSize - 1。

下面是一个更合理的形式:


Function GetValue(ByVal pFileName As String, ByVal pLine As Long, ByVal pRow As Long, Optional ByVal pCellSize As Long = 3, Optional ByVal pLoadLength = 1, Optional ByVal pLineLength As Long = 20, Optional ByVal pStart As Long = 1) As String
  'pFileName    文件名。必要参数。
  'pLine        行号。必要参数。从0开始计算,第一行编号为0。
  'pRow         列号。必要参数。从0开始计算,第一列编号为0。
  'pCellSize    元长。可选参数。单个数值字节长度,默认值为3。
  'pLoadLength  读长。可选参数。单个数据的有效长度。默认为1(从0计算,读两个字节设置为1)
  'pLineLength  行宽。可选参数。默认值为20字节(3×6+2)。
  'pStart       地址偏移。可选参数。默认值为1,从第一字节开始读。
  '返回值       指定行列的数值(2位数值,末尾带空格)
  Dim tOutString As String
  Dim tOutBytes() As Byte
  
  Dim tFileNumber As Integer
  Dim tFileStart As Long
    
  ReDim tOutBytes(pLoadLength)
  
  tFileNumber = FreeFile
  
  tFileStart = pLine * pLineLength + pRow * pCellSize + pStart
  
  Open pFileName For Binary As #tFileNumber
    Get #tFileNumber, tFileStart, tOutBytes()
  Close #tFileNumber
  
  tOutString = StrConv(tOutBytes(), vbUnicode)
  
  GetValue = tOutString
End Function


但我上述函数有一个致命缺点:每一次读文件,它都要Open、Close文件。这样会严重降低大量数据读取时的效率。但由于LZ是个新手,且对速度需求应该不大。为了新手容易使用,所以设计成这种易用但效率差的形式。

效率高的形式是这样的:


Function GetValue(ByVal pFileNumber As Integer, ByVal pLine As Long, ByVal pRow As Long, Optional ByVal pCellSize As Long = 3, Optional ByVal pLoadLength = 1, Optional ByVal pLineLength As Long = 20, Optional ByVal pStart As Long = 1) As String
  'pFileNumber  文件号。必要参数。是一个已Open打开文件的操作号。
  'pLine        行号。必要参数。从0开始计算,第一行编号为0。
  'pRow         列号。必要参数。从0开始计算,第一列编号为0。
  'pCellSize    元长。可选参数。单个数值字节长度,默认值为3。
  'pLoadLength  读长。可选参数。单个数据的有效长度。默认为1(从0计算,读两个字节设置为1)
  'pLineLength  行宽。可选参数。默认值为20字节(3×6+2)。
  'pStart       地址偏移。可选参数。默认值为1,从第一字节开始读。
  '返回值       指定行列的数值(2位数值,末尾带空格)
  Dim tOutString As String
  Dim tOutBytes() As Byte
  
  Dim tFileStart As Long
    
  ReDim tOutBytes(pLoadLength)
    
  tFileStart = pLine * pLineLength + pRow * pCellSize + pStart
  
  Get #pFileNumber, tFileStart, tOutBytes()
  
  tOutString = StrConv(tOutBytes(), vbUnicode)
  
  GetValue = tOutString
End Function


在使用的时候要先打开文件,然后把这个函数放在循环当中对数据进行读取,最后关闭文件。比如下面这样:


Private Sub Command2_Click()
  Dim tI As Long
  Dim tJ As Long
  Dim tFN As Integer
  tFN = FreeFile
  Open "Test.txt" For Binary As #tFN
  For tI = 0 To 3
    For tJ = 0 To 5
      Debug.Print GetValue(tFN, tI, tJ)
    Next
    Debug.Print
  Next
  Close #tFN
End Sub

--------------------编程问答--------------------
引用 18 楼 kitegirl 的回复:
......
他如果会UE分析文件,就不会在这种问题上纠缠不清了。
......

确实也是.

不过话说回来,对文件进行二进制操作,很多时候都能提高效率.

像数据库那些,能在上层封装为简单的对象,还能保证性能,真挺不容易的.
补充:VB ,  基础类
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,