views:

763

answers:

4

I want to use a FileStream and seek from the beginning of the file while moving forward in the file .01% of the file size at a time.

So I want to seek to a position in the file, read the entire line, if it matches my criteria I am done. If not, I seek ahead another .01.

C# is OK but VB.NET preferred.

I used to do it something like this in VB6...

            FileOpen(1, CurrentFullPath, OpenMode.Input, OpenAccess.Read, OpenShare.Shared)
        Dim FileLength As Long = LOF(1)

        For x As Single = 0.99 To 0 Step -0.01
            Seek(1, CInt(FileLength * x))
            Dim S As String = LineInput(1)
            S = LineInput(1)
            filePosition = Seek(1)
            If filePosition < 50000 Then
                filePosition = 1
                Exit For
            End If
            V = Split(S, ",")
            Dim MessageTime As Date = CDate(V(3) & " " & Mid$(V(4), 1, 8))
            Dim Diff As Integer = DateDiff(DateInterval.Minute, MessageTime, CDate(RequestedStartTime))
            If Diff >= 2 Then
                Exit For
            End If
        Next

But I don't want to use FileOpen, I want to use a FileStream.

Any help is greatly appreciated!

+1  A: 

what bout something like this (C# version):

using (var file = System.IO.File.OpenText(filename))
{
     while (!file.EndOfStream)
     {
          string line = file.ReadLine();
          //do your logic here
          //Logical test - if true, then break
     }
}

EDIT: VB version here (warning - from a C# dev!)

Using file as FileStream = File.OpenText(filename)
    while Not file.EndOfStream
         Dim line as string = file.ReadLine()
  ''//Test to break
  ''//exit while if condition met
    End While
End Using
Josh E
Thanks Josh but the file is way to big to go through it line by line. I need to be able to seek to a position in the file.
Doug
A: 

I normally prefer vb.net, but C#'s iterator blocks are slowly winning me over:

public static IEnumerable<string> SkimFile(string FileName)
{
    long delta = new FileInfo(FileName).Length / 100;
    long position = 0;

    using (StreamReader sr = new StreamReader(FileName))
    {
        while (position < 100)
        {
            sr.BaseStream.Seek(position * delta, SeekOrigin.Begin);
            yield return sr.ReadLine();
            position++;
        }
    }
}

Put it in a class library project and use it from vb like this:

Dim isMatch as Boolean = False
For Each s As String in SkimFile("FileName.txt")
    If (RequestedDate - CDate(s.SubString(3,11))).Minutes > 2 Then
       isMatch = True
       Exit For
    End If
Next s

(I took some liberties with you criteria (assumed fixed-width values rather than delimited) to make the example easier)

Joel Coehoorn
Joel, I like this but I should have specified that I need to use .NET 2.0. Is your code 2.0?
Doug
No, you need 3.5 for the lamdba function and the .Any() extension. But the C# part is all 2.0. Give 5 minutes and I'll update the vb to make it 2.0 compatible as well.
Joel Coehoorn
Also, I should warn you: that code was typed directly into the reply window and is completely untested.
Joel Coehoorn
And the seek is likely to end up in the middle of a line somewhere, so you'll only get a partial line per read.
Joel Coehoorn
Oops: forgot about the var keyword. C# is now 2.0 compatible as well.
Joel Coehoorn
A: 

There's an example on MSDN.

Edit in response to comment:

I must admit I'm a bit confused, as you seemed insistant on using a buffered FileStream, but want to read a file a line at a time? You can do that quite simply using a StreamReader. I don't know VB, but in C# it would be something like this:

using (StreamReader sr = File.OpenText(pathToFile)) 
{
    string line = String.Empty;
    while ((line = sr.ReadLine()) != null) 
    {
        // process line
    }
}

See http://msdn.microsoft.com/en-us/library/system.io.file.aspx.

Dan Diplo
Thanks Dan, but I wanted to read a line and not char by char. I think that is what the MSDN example is doing. I could not get it to work for me.
Doug
+1  A: 

This is a more or less direct conversion of your code, where we use FileStream.Position to specify where in the file to read:

Dim fileStream As New System.IO.FileStream(CurrentFullPath, IO.FileMode.Open, IO.FileAccess.Read)
Dim streamReader As New System.IO.StreamReader(fileStream)
For x As Single = 0.99 To 0 Step -0.01
  fileStream.Position = CLng(fileStream.Length * x)
  Dim S As String = streamReader.ReadLine()
  '... etc.
Next
'...
streamReader.Close()

Update: Here's a similar implementation with Using / End Using:

Using streamReader As System.IO.StreamReader = System.IO.File.OpenText(CurrentFullPath)
  For x As Single = 0.99 To 0 Step -0.01
    streamReader.BaseStream.Position = CLng(streamReader.BaseStream.Length * x)
    Dim S As String = streamReader.ReadLine()
    '... etc.
  Next
End Using
Bernhof
-1 for no Using / End Using. Also, did you test this code? How does StreamReader feel about having the stream position change out from under it? You should also test to see what it does if the position changes to just before the newline, and when it changes to be inside a CR + LF pair.
John Saunders
You're right, this isn't exactly the ideal implementation, but I wrote it this way in order for Doug to easily see the similarities/differences between his original code and the new FileStream approach. And yes, I've tested the code, although my test was somewhat superficial. The StreamReader does not seem to mind that the position is changed, since it just reads from the current stream position.
Bernhof
The advice about checking if the position is currently between and CR/LF pair is good. Does the original code handle this situation?
Bernhof
I've updated the answer with a Using / End Using example.
Bernhof