如何在VB.NET中正确读取随机访问文件

时间:2012-09-17 04:04:29

标签: vb.net

我正在尝试读取随机访问文件,但是我在第一个文件Error 5 (unable to read beyond end of the stream)上收到以下错误。我不确定我在这里做错了什么,我该如何解决这个问题?

Structure StdSections
    'UPGRADE_WARNING: Fixed-length string size must fit in the buffer. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="3C1E4426-0B80-443E-B943-0627CD55D48B"'
    <VBFixedString(15), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=15)> Public A() As Char 'BEAM  --- complete beam designation          15
    'UPGRADE_WARNING: Fixed-length string size must fit in the buffer. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="3C1E4426-0B80-443E-B943-0627CD55D48B"'
    <VBFixedString(2), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=2)> Public B() As Char 'DSG   --- shape  ie "W" or "C"                2
    Dim C As Single 'DN    --- nominal depth of section            4
    Dim d As Single 'WGT   --- weight                              4
    .
    .
    .
End structure
''Note 'File1'is the existing RAF and holds complete path!

        Dim i,ffr,fLength,lastmembNo as integer
        sectionFound = False
        Dim std As new StdSections 
        fLength = Len(std)
        If fLength = 0 Then fLength = 168 ' 177
        ffr = FreeFile()
        FileOpen(ffr, File1, OpenMode.Random, OpenAccess.Read, OpenShare.LockRead, fLength)
        lastmembNo = CInt(LOF(ffr)) \ fLength

        For i = 1 To lastmembNo
            FileGet(ffr, std, i)
            >>Error 5 (unable to read beyond end of the stream) <<<                  
            If Trim(memberID) = Trim(std.A) Then
                    sectionFound = True
                end if
        next i

3 个答案:

答案 0 :(得分:1)

哇Freefile!这是过去的爆炸!

我还没有真正使用VB.NET中的旧OpenFile等文件访问方法,所以我只是在猜测,但在.NET中,许多变量类型的大小都发生了变化。例如一个整数现在是32位(4字节),我认为布尔值是不同的,虽然单个仍然是4个字节。

此外,.NET中的字符串默认为Unicode,而不是ASCII,因此您不能依赖.NET字符串变量中的1个字符= 1个字节。实际上,.NET在运行之前就会在PC上执行“JIT编译”程序,所以你不能像过去那样在内存中真正布局结构。

如果你想切换到新的“Stream”对象,这里有一些代码可以帮助你入门:

    Dim strFilename As String = "C:\Junk\Junk.txt"
    Dim strTest As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Call My.Computer.FileSystem.WriteAllText(strFilename, strTest, False)
    Dim byt(2) As Byte
    Using fs As New FileStream(strFilename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)
      fs.Seek(16, SeekOrigin.Begin)
      fs.Read(byt, 0, 3)
      Dim s As String = Chr(byt(0)) & Chr(byt(1)) & Chr(byt(2))
      MsgBox(s)
      fs.Seek(5, SeekOrigin.Begin)
      fs.Write(byt, 0, 3)
    End Using
    Dim strModded As String = My.Computer.FileSystem.ReadAllText(strFilename)
    MsgBox(strModded)

我不会因为保留旧方法而责怪你:使用新方法,你需要定义一个类,然后有一个自定义例程来从Byte()转换为类的属性。比简单地将文件中的字节加载到内存中更多的工作。

答案 1 :(得分:0)

好的,我认为您应该切换到“.NET方式”,如下所示:

Imports System.IO
Imports System.Xml

Public Class Form1

  Public Const gintRecLen_CONST As Integer = 177

  Class StdSections2
    Private mstrA As String
    Public Property A() As String
      Get
        Return mstrA
      End Get
      Set(ByVal value As String)
        If value.Length <> 15 Then
          Throw New Exception("Wrong size")
        End If
        mstrA = value
      End Set
    End Property

    Private mstrB As String
    Public Property B() As String
      Get
        Return mstrB
      End Get
      Set(ByVal value As String)
        If value.Length <> 2 Then
          Throw New Exception("Wrong size")
        End If
        mstrB = value
      End Set
    End Property

    Public C(39) As Single

    Public Shared Function FromBytes(byt() As Byte) As StdSections2
      Dim output As New StdSections2

      If byt.Length <> gintRecLen_CONST Then
        Throw New Exception("Wrong size")
      End If
      For i As Integer = 0 To 14
        output.mstrA &= Chr(byt(i))
      Next i
      For i As Integer = 15 To 16
        output.mstrB &= Chr(byt(i))
      Next i
      For i As Integer = 0 To 39
        Dim bytTemp(3) As Byte
        output.C(i) = BitConverter.ToSingle(byt, 17 + 4 * i)
      Next i
      Return output
    End Function
  End Class

  Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.


  End Sub


  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim strFilename As String = "C:\Junk\Junk.txt"
    Dim strMemberID As String = "foo"
    Dim intRecCount As Integer = CInt(My.Computer.FileSystem.GetFileInfo(strFilename).Length) \ gintRecLen_CONST
    Dim blnSectionFound As Boolean = False
    Using fs As New FileStream(strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
      For intRec As Integer = 0 To intRecCount - 1
        Dim intRecPos As Integer = gintRecLen_CONST * intRec
        fs.Seek(intRecPos, SeekOrigin.Begin)
        Dim byt(gintRecLen_CONST - 1) As Byte
        fs.Read(byt, 0, gintRecLen_CONST)
        Dim ss2 As StdSections2 = StdSections2.FromBytes(byt)
        'MsgBox(ss2.A & ":" & ss2.C(3)) 'debugging
        If strMemberID.Trim = ss2.A.Trim Then
          blnSectionFound = True
          Exit For
        End If
      Next intRec
    End Using
    MsgBox(blnSectionFound.ToString)

  End Sub
End Class

我们定义了一个名为StdSections2的类,它使用.NET字符串和Singles数组。我们到处都使用基于0的数组。我们使用新的FileStream对象加载文件,并使用Seek()命令查找我们想要的位置。然后我们从文件中加载原始字节,并使用Chr()和BitConverter.ToSingle()将原始字节转换为字符串和单个字节。

答案 2 :(得分:0)

我不确定你的例子,但是这个有效:

Public Class Form1

Const maxLenName = 30

Structure person
    <VBFixedString(maxLenName)> Dim name As String
    Dim age As Byte
End Structure

Private Sub Form1_Load(sender As [Object], e As EventArgs) Handles MyBase.Load

    Dim entryIn As New person
    Dim recordLen As Integer = Len(entryIn)
    Dim entry As person

    If FileIO.FileSystem.FileExists("test.raf") Then Kill("test.raf")
    FileOpen(1, "test.raf", OpenMode.Random,,, recordLen)

    'write
    entry.name = LSet("Bill", maxLenName)
    entry.age = 25
    FilePut(1, entry, 6) 'write to 6th record

    'read
    Dim nRecords As Integer = LOF(1) \ recordLen
    FileGet(1, entryIn, nRecords)
    FileClose(1)

    Dim personName As String = RTrim(entryIn.name)
    Dim personAge As Byte = entryIn.age

    MsgBox(personName & "'s age is " & personAge)
End Sub
End Class