还是随机文件读写问题
问题代码: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 字节)。
如果你还是坚持现有的随机读方式的话,就至少应该去掉所有的回车换行符,也可以去掉所有的空格。
当然,你目前改动最少的方法,就是将所有换行符统一。 --------------------编程问答--------------------
汗,我那只是一个例子,具体的字节数这些你自己用UE以十六进制打开分析一下嘛!!人不能懒到这程度.... --------------------编程问答--------------------
按楼主 11F 的说法,应该有这个问题。
--------------------编程问答-------------------- 小仙妹回复真有意思~~~~~~~
方法不错,顶…………
--------------------编程问答--------------------
他如果会UE分析文件,就不会在这种问题上纠缠不清了。看起来很是着急啊……
不过话又说回来,谁都是从这个时候过来的。我上初中时候刚学BASIC,也是先接触随机文件。因为当时觉得二进制文件很“可怕”。现在觉得二进制是最简单、最直接的办法。
从本质上看来说:他要求根据行、列取固定位数据的需求是一种三维定位式的读文件函数。
第一维是数据单元;第二维是列;第三维是行。
只要是这种固定行列、固定数据位数的文本格式数据,不同的行列、起始位置仅仅是几个参数的区别。这个函数都可以通用。
我为了图省事,省略了一个可选参数,就是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
--------------------编程问答--------------------
确实也是.
不过话说回来,对文件进行二进制操作,很多时候都能提高效率.
像数据库那些,能在上层封装为简单的对象,还能保证性能,真挺不容易的.
补充:VB , 基础类