Results 1 to 13 of 13

Thread: lineinput command not working as described in documentation.

  1. #1

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    lineinput command not working as described in documentation.

    I'm pretty new to VB.net. I've been tasked to convert some Windows Microsoft VB6 programs to Windows Microsoft VB.net in Visual Studio 2015. I have a program in VB6 that uses LINE INPUT to read in lines off of a file. It creates one line for each line in the file up to a CRLF. That same program is running in VB.net with slight conversions (ie. LINE INPUT command to LINEINPUT). With the VB.net version I've discovered that it READS one character at a time until it hits a LF (Line Feed). In VB6 it read past the LF, till it hits a CRLF. Now the documentation for VB.net says the following in quotes. The documentation for the VB6 documentation is basically identical for how it reads the file.

    "The LineInput function reads from a file one character at a time until it encounters a carriage return (Chr(13)) or carriage return/line feed (Chr(13) + Chr(10)) sequence. Carriage return/line feed sequences are skipped instead of appended to the character string."

    Here is a sample program in VB.net below that shows a line is done being read once it hits a LF. My input file looks like the file in RED. So there is a LF in the middle of the second line right after the period. Now when I run this through the program the output is what is in BLUE. You can see when the program reads the second line, it stops after 'This is the second line' and puts that in the TextLine variable. Then the next iteration it picks up 'There is a LF between the second line and this one'. So this causes a problem for files that have LF's in them. Normally I would tell applications to remove the Line Feeds but they have been established for quite some time working with VB6 programs that are working correctly. I have to migrate to VB.net so I'm frustrated the VB.net is not working as VB6 did and as the documentation says it should.
    So am I reading the documentation wrong? Is there something I'm reading wrong. A few suggested scanning the file and removing the LF's, but I don't want to modify the file for applications. Bottom line is I only want the lines to "break" when it encounters a CRLF or CR. Any help would really be appreciated.

    Gary

    This is the first line ending with CRLF.
    This is the second line. There is a LF between the second line and this one.
    This is the third line.


    This is the first line ending with CRLF.
    This is the second line.
    There is a LF between the second line and this one.
    This is the third line.


    Module Module1
    Sub Main()

    Dim TextLine As String
    ' Open file.
    FileOpen(1, "file.txt", OpenMode.Input)
    ' Loop until end of file.
    While Not EOF(1)
    ' Read line into variable.
    TextLine = LineInput(1)
    ' Print to the console.
    System.Console.WriteLine(TextLine)
    End While
    FileClose(1)

    End Sub
    End Module

  2. #2
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,464

    Re: lineinput command not working as described in documentation.

    Use IO.File.ReadAllLines instead of all that legacy code

  3. #3
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: lineinput command not working as described in documentation.

    That won't fix the main problem though.
    ReadAllLines will still see the embedded LF character and treat it as a line termination, so the lines with embedded LFs will be split up into separate lines by ReadAllLines, the same as LineInput does in VB.Net.

    I think you will need to read all the text and then split it on vbcrlf yourself.
    Code:
        Dim a As String = IO.File.ReadAllText("c:\c\tstLineTerminators.txt")
        Dim b() As String = a.Split(vbCrLf)
    The last "line" of the b array in the example code may be empty if the file ended with a CrLF.

    p.s. I reread the post and the above doesn't handle the single CR line termination though. I guess you can add a line to change vbCrLf to vbCr first, then split on vbCr.
    Code:
        Dim a As String = IO.File.ReadAllText("c:\c\tstLineTerminators.txt")
        a = a.Replace(vbCrLf, vbCr)
        Dim b() As String = a.Split(vbCr)
    Last edited by passel; Jun 28th, 2017 at 10:00 AM.

  4. #4
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: lineinput command not working as described in documentation.

    Unfortunately, the documentation doesn't quite say what LineInput() does when it encounters an LF. So a snarky documentation writer might point out that is "undefined behavior". Let's not be snarky, let's try and get something working.

    It is unfortunately true that while you're converting these VB6 programs, you're going to have to rewrite some bits. A ton of VB6 code constructs translate perfectly well to VB .NET, even if they are archaic in modern eyes. There are also quite a few concepts that don't translate well, and occasionally you find somewhere like this that functionality has changed ever so subtly. You're not going to get out of this project without rewriting some pieces. You're not going to talk MS into fixing this as a bug because, at this point, the .NET LineInput() function is 14 years old so it has its own legacy. You might've had more luck in 2003-2005, when VB developers were still shaping the development of .NET.

    So OK. That's not helping you. Let's talk about writing something that works and is a kind of easy drop-in replacement.

    You'll have to stop using the built-in LineInput() and replace it with one that "properly" handles LF for your purposes. You can't trust the built-in .NET types because they tend to treat ANY line-ending character as a line ending. ReadAllText() is sometimes not an option if you're parsing very, large files. So I think our only choice is to get down and dirty with the StreamReader class and write our own kind of line-reading type.

    But that won't really be a drop-in replacement to your code, because there's not a way to make it work with the legacy VB "I use numbers to represent file handles" practice. Hm. It'd be fun to write a fully-compatible drop-in replacement, but I think it would be best to move to the .NET APIs. Some of this is because I'm not familiar enough with the legacy APIs to be confident I can get to the end with something functional.

    So I might use something a bit like this:
    Code:
    Public Class SpecialLineReader
        Implements IDisposable
    
        Private _reader As StreamReader
    
        Public ReadOnly Property IsEndOfStream As Boolean
            Get
                Return _reader.EndOfStream
            End Get
        End Property
    
        Public Sub New(ByVal filePath As String)
            _reader = New StreamReader(filePath)
        End Sub
    
        ' Treats LF like a normal whitespace character. Treats both CR and CRLF as line terminators.
        ' Returned string does not include line terminators.
        Public Function ReadLine() As String
            If _reader Is Nothing OrElse IsEndOfStream Then
                Return Nothing
            End If
    
            Dim currentLine As New StringBuilder()
            While Not IsEndOfStream
                Dim nextCharacter As Char = Convert.ToChar(_reader.Read())
                If nextCharacter = vbCr Then
                    If Convert.ToChar(_reader.Peek()) = vbLf Then
                        ' Found CRLF, advance past the LF
                        _reader.Read()
                        Exit While
                    Else
                        ' Just CR
                        Exit While
                    End If
                End If
    
                currentLine.Append(nextCharacter)
            End While
    
            Return currentLine.ToString()
        End Function
    
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            If _reader IsNot Nothing Then
                _reader.Dispose()
            End If
        End Sub
    
    End Class
    The ReadLine() sub is the important part. If you've already reached the end of the stream, it returns Nothing. Be sure to check for that. It starts reading one character at a time. If the character is not CR, it gets appended to the "current line". If the character is CR, it checks for LF and moves past it if present, then quits reading characters. If it reaches the end of the stream before finding CR, it quits reading characters. When it decides to quit reading, it returns the "current line".

    So in your code example, you would use this class like so:
    Code:
    Using specialReader As New SpecialLineReader("file.txt")
        While Not specialReader.IsEndOfStream
            Dim line As String = specialReader.ReadLine()
            Console.WriteLine(line)
        End While
    End Using
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  5. #5

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    Re: lineinput command not working as described in documentation.

    Thanks for the input. I had a bad feeling there wouldn't be an easy drop in fix. The one thing in your code you say is treat LF just like a Normal Whitespace character. I would prefer that it just be ignored. So if the line was
    "This is the lineLFwe are talking about" where LF is the line feed, after this is read in and spit out we would end up with "This is the linewe are talking about". I will have to play around with this, since I'm not that good with VB.net, I will have to mess with it to get it right. Why do they add vb in front of LF and CR? Does vb just stand for visual basic?? Sorry probably a stupid question.
    Gary

  6. #6
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,464

    Re: lineinput command not working as described in documentation.

    Passel gave you an easy answer in post #3

  7. #7
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: lineinput command not working as described in documentation.

    While I agree passel's solution is easy, it has to read the entire file into RAM to do anything. And it needs enough room for multiple copies: one for ReadAllText(), one for Replace() (plus probably some intermediate memory), and one for Split(). That matters when files start getting pretty big, and won't perform as quickly.

    Of course, if we're talking smallish files in the KB range, there's no noticeable difference. I'm usually parsing 100MB+ files so I tend to notice these things.

    I will have to play around with this, since I'm not that good with VB.net, I will have to mess with it to get it right.
    It's not that big a change! Inside my ReadLine() function, the loop reads a character, then decides what it wants to do with it. If you want to ignore LF, we just need a new case:
    Code:
    If nextCharacter = vbCr Then
        ...
    ElseIf nextCharacter = vbLf Then
        ' Do nothing with LF
    End If
    Now that loop does something special for CR, something special for LF ('do nothing' is something special!), and adds any other character to the line.

    Why do they add vb in front of LF and CR? Does vb just stand for visual basic?? Sorry probably a stupid question.
    There's no stupid questions! But you guessed right. These constants live in a module that's loaded by default in your files. They probably could be named 'cr' or 'lf', but I think they were named 'vbCr' and 'vbLf' for two reasons:
    • Very short, two-letter names are generally discouraged in APIs. This would be different if they weren't in a Module and had to be fully qualified like StringConstants.LF. Such is not the case.
    • These constants are provided via VB libraries and aren't part of the "proper" .NET Framework. Every other constant like that begins with 'vb' to help distinguish it.


    I don't tend to like to use them, but in this case they were convenient and I thought they would be familiar. I rarely see them outside a topic about converting from VB6.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  8. #8
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: lineinput command not working as described in documentation.

    {edit} Sitten replied while I was composing so mostly redundant information here.{/edit}

    I'm not sure now. The code in post #3 leaves the single LFs in the line, which I'm assuming legacy VB's Line Input did.
    But if you would rather remove them, as your comments in post #5 seems to suggest, then you could just remove them as was suggested.
    You're not modifying the file, you're modifying the string read from the file.
    The code would be almost the same as was in post #3.
    Code:
        Dim a As String = IO.File.ReadAllText("c:\c\tstLineTerminators.txt")
        a = a.Replace(vbLf, "")
        Dim b() As String = a.Split(vbCr)
    Sitten Spynne does have a point about dealing with large files, and using his function wouldn't be that much of a change when you port the VB6 code. Once you have the Class defined, you're essentially just replacing the "Line Input" calls with "specialReader.ReadLine()" calls, so should be an easy modification to make. And if you really do want to filter the Line Feeds out, then that is an easy change to Sitten Spynne's class example as well.

  9. #9

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    Re: lineinput command not working as described in documentation.

    Again thanks for more help. I will start working on this as soon as I'm back from vacation . I do really appreciate all the help, since this is foreign to me!!

    Gary

  10. #10

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    Re: lineinput command not working as described in documentation.

    So with the code that Sitten provided, I was able to make a sample program to read files and test the cr,crlf,lf issue and get around it. I do have a question or two. Does the routine that is provided close the file? With my normal file processing where I use numbers, I specifically say 'fileclose'. I don't see the code do anything like that. I see a dispose routine, but it is never called, at least not directly??
    Thanks,
    Gary

  11. #11
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: lineinput command not working as described in documentation.

    The Using statement is syntax sugar for a Try..Finally block with disposal.

    So my code that looks like this:
    Code:
    Using specialReader As New SpecialLineReader("file.txt")
        ...
    End Using
    Behaves as if I wrote it like this:
    Code:
    Dim specialReader As SpecialLineReader
    Try
        specialReader = New SpecialLinereader("file.txt")
        ...
    Finally
        If specialReader IsNot Nothing
            specialReader.Dispose()
        End If
    End Try
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  12. #12

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    Re: lineinput command not working as described in documentation.

    Excellent!! Thank you.

  13. #13

    Thread Starter
    New Member
    Join Date
    Jun 2017
    Posts
    8

    Re: lineinput command not working as described in documentation.

    Thanks to everyone that helped. I basically used the code given and made it work. Sorry I took so long to say thanks, but I didn't want to jinx myself and say thanks too soon, then have it not work.
    Gary

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width