Hi I am trying to search for a line which contains whats the user inputs in a text box and display the whole line. My code below doesnt display a messsagebox after the button has been clicked and i am not sure if the record has been found
Dim filename, sr As String
filename = My.Application.Info.DirectoryPath + "\" + "mul.txt"
Dim file As String()
Dim i As Integer = 0
file = IO.File.ReadAllLines(filename)
Dim found As Boolean
Dim linecontain As Char
sr = txtsr.ToString
For Each line As String In file
If line.Contains(sr) Then
found = True
Exit For
End If
i += 1
If found = True Then
MsgBox(line(i))
End If
Next
End Sub
You should be calling ReadLines here rather than ReadAllLines. The difference is that ReadAllLines reads the entire file contents into an array first, before you can start processing any of it, while ReadLines doesn't read a line until you have processed the previous one. ReadAllLines is good if you want random access to the whole file or you want to process the data multiple times. ReadLines is good if you want to stop processing data when a line satisfies some criterion. If you're looking for a line that contains some text and you have a file with one million lines where the first line matches, ReadAllLines would read all one millions lines whereas ReadLines would only read the first.
So, here's how you display the first line that contains specific text:
For Each line In File.ReadLines(filePath)
If line.Contains(substring) Then
MessageBox.Show(line)
Exit For
End If
Next
With regards to your original code, your use of i makes no sense. You seem to be using i as a line counter but there's no point because you're using a For Each loop so line contains the line. If you already have the line, why would you need to get the line by index? Also, when you try to display the message, you are using i to index line, which means that you're going to get a single character from the line rather than a single line from the array. If the index of the line is greater than the number of characters in the line then that is going to throw an IndexOutOfRangeException, which I'm guessing is what's happening to you.
This is what comes from writing code without knowing what it actually has to do first. If you had written out an algorithm before writing the code, it would have been obvious that the code didn't implement the algorithm. If you have no algorithm though, you have nothing to compare your code to to make sure that it makes sense.
I have a simple .txt log file to which an application adds lines as it does its work. The lines consist of a timestamp and a variable-length text:
17-06-25 06:37:43 xxxxxxxxxxxxxxx
17-06-25 06:37:46 yyyyyyy
17-06-25 06:37:50 zzzzzzzzzzzzzzzzzzzzzzzzzzzz
...
I need to extract all lines with a timestamp greater than a certain date-time. This typically is about the last, say, 20-40 log entries (lines).
The problem is, that the file is large and growing.
If all lengths would be equal, I'd invoke a binary search. But they aren't, and so I end up using something like:
Private Sub ExtractNewestLogs(dEarliest As Date)
Dim sLine As String = ""
Dim oSRLog As New StreamReader(gsFilLog)
sLine = oSRLog.ReadLine()
Do While Not (sLine Is Nothing)
Debug.Print(sLine)
sLine = oSRLog.ReadLine()
Loop
End Sub
which, well, isn't really fast.
Is there a method with which I can read such files "backwards", i.e., last line first? If not, what other option do I have?
The function below will return the last x number of characters from a file as an array of strings using a binary reader. You can then pull the last records that you want much more quickly than reading the entire log file. You can fine tune the number of bytes to read according to a rough approximation of how many bytes are taken by the last 20-40 log entries. On my pc - it took <10ms to read the last 10,000 characters of a 17mb text file.
Of course this code assumes that your log file is plain ascii text.
Private Function ReadLastbytes(filePath As String, x As Long) As String()
Dim fileData(x - 1) As Byte
Dim tempString As New StringBuilder
Dim oFileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim oBinaryReader As New BinaryReader(oFileStream)
Dim lBytes As Long
If oFileStream.Length > x Then
lBytes = oFileStream.Length - x
Else
lBytes = oFileStream.Length
End If
oBinaryReader.BaseStream.Seek(lBytes, SeekOrigin.Begin)
fileData = oBinaryReader.ReadBytes(lBytes)
oBinaryReader.Close()
oFileStream.Close()
For i As Integer = 0 To fileData.Length - 1
If fileData(i)=0 Then i+=1
tempString.Append(Chr(fileData(i)))
Next
Return tempString.ToString.Split(vbCrLf)
End Function
I attempted a binary search anyway, eventhough the file has not static line lengths.
First some considerations, then the code:
Sometimes it is needed, that the last n lines of a log file are extracted, based on an ascending sort key at the beginning of the line. The key really could be anything, but in log files typically represents a date-time, usually in the format YYMMDDHHNNSS (possibly with some interpunction).
Log files typically are text based files, consisting of multiple lines, at times millions of them. Often log files feature fixed-length line widths, in which case a specific key is quite easy to access with a binary search. However, probably also as often, log files have a variable line width. To access these, one can use an estimate of an average line width in order to calculate a file position from the end, and then process from there sequentially to the EOF.
But one can employ a binary approach also for this type of files, as demonstrated here. The advantage comes in, as soon as file sizes grow. A log file's maximum size is determined by the file system: NTFS allows for 16 EiB (16 x 2^60 B), theoretically; in practice under Windows 8 or Server 2012, it's 256 TiB (256 x 2^40 B).
(What 256 TiB actually means: a typical log file is designed to be readable by a human and rarely exceeds many more than 80 characters per line. Let's assume your log file logs along happily and completely uninterrupted for astonishing 12 years for a total of 4,383 days at 86,400 seconds each, then your application is allowed to write 9 entries per millisecond into said log file, to eventually meet the 256 TiB limit in its 13th year.)
The great advantage of the binary approach is, that n comparisons suffice for a log file consisting of 2^n bytes, rapidly gaining advantage as the file size becomes larger: whereas 10 comparisons are required for file sizes of 1 KiB (1 per 102.4 B), there are only 20 comparisons needed for 1 MiB (1 per 50 KiB), 30 for 1 GiB (1 per 33⅓ MiB), and a mere 40 comparisons for files sized 1 TiB (1 per 25 GiB).
To the function. These assumptions are made: the log file is encoded in UTF8, the log lines are separated by a CR/LF sequence, and the timestamp is located at the beginning of each line in ascending order, probably in the format [YY]YYMMDDHHNNSS, possibly with some interpunction in between. (All of these assumptions could easily be modified and cared for by overloaded function calls.)
In an outer loop, binary narrowing is done by comparing the provided earliest date-time to match. As soon as a new position within the stream has been found binarily, an independent forward search is made in an inner loop to locate the next CR/LF-sequence. The byte after this sequence marks the start of the record's key being compared. If this key is larger or equal the one we are in search for, it is ignored. Only if the found key is smaller than the one we are in search for its position is treated as a possible condidate for the record just before the one we want. We end up with the last record of the largest key being smaller than the searched key.
In the end, all log records except the ultimate candidate are returned to the caller as a string array.
The function requires the import of System.IO.
Imports System.IO
'This function expects a log file which is organized in lines of varying
'lengths, delimited by CR/LF. At the start of each line is a sort criterion
'of any kind (in log files typically YYMMDD HHMMSS), by which the lines are
'sorted in ascending order (newest log line at the end of the file). The
'earliest match allowed to be returned must be provided. From this the sort
'key's length is inferred. It needs not to exist neccessarily. If it does,
'it can occur multiple times, as all other sort keys. The returned string
'array contains all these lines, which are larger than the last one found to
'be smaller than the provided sort key.
Public Shared Function ExtractLogLines(sLogFile As String,
sEarliest As String) As String()
Dim oFS As New FileStream(sLogFile, FileMode.Open, FileAccess.Read,
FileShare.Read) 'The log file as file stream.
Dim lMin, lPos, lMax As Long 'Examined stream window.
Dim i As Long 'Iterator to find CR/LF.
Dim abEOL(0 To 1) As Byte 'Bytes to find CR/LF.
Dim abCRLF() As Byte = {13, 10} 'Search for CR/LF.
Dim bFound As Boolean 'CR/LF found.
Dim iKeyLen As Integer = sEarliest.Length 'Length of sort key.
Dim sActKey As String 'Key of examined log record.
Dim abKey() As Byte 'Reading the current key.
Dim lCandidate As Long 'File position of promising candidate.
Dim sRecords As String 'All wanted records.
'The byte array accepting the records' keys is as long as the provided
'key.
ReDim abKey(0 To iKeyLen - 1) '0-based!
'We search the last log line, whose sort key is smaller than the sort
'provided in sEarliest.
lMin = 0 'Start at stream start
lMax = oFS.Length - 1 - 2 '0-based, and without terminal CRLF.
Do
lPos = (lMax - lMin) \ 2 + lMin 'Position to examine now.
'Although the key to be compared with sEarliest is located after
'lPos, it is important, that lPos itself is not modified when
'searching for the key.
i = lPos 'Iterator for the CR/LF search.
bFound = False
Do While i < lMax
oFS.Seek(i, SeekOrigin.Begin)
oFS.Read(abEOL, 0, 2)
If abEOL.SequenceEqual(abCRLF) Then 'CR/LF found.
bFound = True
Exit Do
End If
i += 1
Loop
If Not bFound Then
'Between lPos and lMax no more CR/LF could be found. This means,
'that the search is over.
Exit Do
End If
i += 2 'Skip CR/LF.
oFS.Seek(i, SeekOrigin.Begin) 'Read the key after the CR/LF
oFS.Read(abKey, 0, iKeyLen) 'into a string.
sActKey = System.Text.Encoding.UTF8.GetString(abKey)
'Compare the actual key with the earliest key. We want to find the
'largest key just before the earliest key.
If sActKey >= sEarliest Then
'Not interested in this one, look for an earlier key.
lMax = lPos
Else
'Possibly interesting, remember this.
lCandidate = i
lMin = lPos
End If
Loop While lMin < lMax - 1
'lCandidate is the position of the first record to be taken into account.
'Note, that we need the final CR/LF here, so that the search for the
'next CR/LF sequence following below will match a valid first entry even
'in case there are no entries to be returned (sEarliest being larger than
'the last log line).
ReDim abKey(CInt(oFS.Length - lCandidate - 1)) '0-based.
oFS.Seek(lCandidate, SeekOrigin.Begin)
oFS.Read(abKey, 0, CInt(oFS.Length - lCandidate))
'We're done with the stream.
oFS.Close()
'Convert into a string, but omit the first line, then return as a
'string array split at CR/LF, without the empty last entry.
sRecords = (System.Text.Encoding.UTF8.GetString(abKey))
sRecords = sRecords.Substring(sRecords.IndexOf(Chr(10)) + 1)
Return sRecords.Split(ControlChars.CrLf.ToCharArray(),
StringSplitOptions.RemoveEmptyEntries)
End Function
I am currently writing some code which is supposed to read a file and put every line in an array (That works) and then use the .Substring command to split every line from the array into two variables (ID and NAME).
I think that the way I am doing could work but I always get the error - The ID varible is being used before having a value
'Puts every line from file in array
Dim lines As String() = File.ReadAllLines(cleanfile)
Dim ID As String()
Dim NAME As String()
'Supposed to substring every line and split it in ID and NAME
'( ID(1) from lines(1), ID(2) from lines(2), etc. )
'Error starts here for ID and NAME
ID(1 - 40) = lines(1 - 40).Substring(0, 7)
NAME(1 - 40) = lines(1 - 40).Substring(30, 60)
What am I missing? Or is there some error in the syntax?
You can't index an array range the way you seem to be trying to. When you write lines(1 - 40), it's not doing "lines one to forty" - it's doing "line minus thirty-nine."
Your error occurs for similar reason: you appear to be trying to create and set array values "one to forty," but really this is trying to set "value minus thirty-nine." Since you haven't set ID to anything yet, you can't do that: there's no array!
Really what you need is a loop, that will take each line in turn and process it. Fortunately, with Linq, the loop is abstracted away and the code can be more simple.
The simplest way would be something like this:
Dim ID as String()
ID = lines.Select(Function (l as String) As String
return l.SubString(0, 7)
End Function).ToArray()
I'm trying to generate a variable for each line in a .txt file. It works for the first line but not the ones below. Would be awesome if you guys could help me proceed! This is what I have so far. I was thinking of using EOF but I couldn't get it to work -.-
Dim sr As New StreamReader(cleanfile)
Dim coins As String() = sr.ReadLine.Split(Environment.NewLine)
The number of variables must be known at compile-time. However, since the number of lines in the file can vary, you don't know this number at compile-time.
Thus, a separate variable for each line is not the solution for your problem.
Your solution is to use an array. To create one, you can simply use File.ReadAllLines:
Dim lines As String() = File.ReadAllLines(cleanfile)
Then, you can access the lines as lines(0), lines(1) or iterate through them using the For Each statement:
For Each line in lines
' Do something with line
' ...
Next
first time questioner here. Thanks in advance for any help you can give.
I'm trying to read a bunch of data from a spreadsheet, chop it up, then throw it into a database. I would rather not do things this way, but it's a basic reality of dealing with accountant-types. Thankfully these spreadsheet reports are very consistent. Anyway, I'm using LINQ for SQL to handle the object-to-reference stuff and I'm using Microsoft.Office.Interop to get my Excel on.
I read through a directory full of .xls and for each one, I'm opening the file, getting a couple of specific data from some specific cells, and then getting a range of cells to pick out values.
Private Sub ProcessAFile(ByVal f As FileInfo)
thisFile = openApp.Workbooks.Open(f.fullName)
thisMonth = Split(thisChart.Range("D6").Value, "-").Last.Trim
thisFY = thisChart.Range("L7").Value
thisWorkArea = thisChart.Range("A14", "L51").Value2
openApp.Workbooks.Close()
...
thisWorkArea was Dimmed as a global:
Dim thisWorkArea As Object(,)
I'm getting both strings and ints in my range between A14 and L51, so making it an Object array makes sense here. I don't want to go through each row and pick out ranges in Excel, I want to just read it once and then close it.
So I'm getting the following exception:
System.IndexOutOfRangeException was unhandled
Message=Index was outside the bounds of the array.
in this function:
Private Sub fillCurrMonth()
Dim theseRows As Integer() = {0, 2, 3, 5}
For Each i In theseRows
Dim thisMonth As New Month
'make sure category is in Database
thisMonth.Dept = thisDeptName
thisMonth.FY = thisFY
thisMonth.Category = thisWorkArea(i, 0)
...
"Month" above refers to a LINQ entity. It's nothing fancy.
That last line there is where I'm catching the exception. In my watch, I find that thisWorkArea has a length of 456 and (0,0) -> "Inpatient"{String}
So why am I getting this exception? I put this in your expert hands. I'm still rather new to vb.net, so maybe I'm just missing something fundamental.
Excel uses 1-based indicies. This stems from it using VB as it's in-app programming language which traditionally used 1-based indicies.
You'll find Excel returned an array defined as thisWorkArea(1 To 11, 1 To 37) As Object
'Fix excel's 1 based index
Dim oData(UBound(xData) - 1, UBound(xData, 2) - 1) As Object
For i As Integer = 1 To UBound(xData)
For ii As Integer = 1 To UBound(xData, 2)
oData(i - 1, ii - 1) = xData(i, ii)
Next
Next