Is there a low cost way to test the first line in a file for a LF terminator instead of a CRLF?
We receive a lot of files from customers and a few of them send us EOL terminators as LF's instead of CRLF. We're using SSIS to import so I need the row terminators to be the same. (when I open the file in Notepad++ I can see the lines end with LF instead of CRLF)
If I read the first line of a file into a StreamReader ReadLine, the line looks like it doesn't contain any type of terminator. I tested for line.Contains(vbLf) and vbCr and vbCrLf and all came back false.
I think I can read the entire file into memory and test for vbLf but some of the files we receive are pretty large (25MB) and it seems like a huge resource waste just to check the line terminator in the first row. Worst case is I can rewrite every line in every file we receive with a line + System.Environment.NewLine but again that a waste for files that already use CRLF.
EDIT Final Code Below based on the answer from #icemanind (SSIS script task passing in directory variable)
Public Sub Main()
'Gets the directory and a listing of the files and calls the sub
Dim sPath As String
sPath = Dts.Variables("User::DataSourceDir").Value.ToString
Dim sDirectory As String = sPath
Dim dirList As New DirectoryInfo(sDirectory)
Dim fileList As FileInfo() = dirList.GetFiles()
For Each fileName As FileInfo In fileList
ReplaceBadEol(fileName)
Next
Dts.TaskResult = ScriptResults.Success
End Sub
'Temp filename postfix
Private Const fileNamePostFix As String = "_Temp.txt"
'Tests to see if the file has a valid end of line terminator and fixes if it doesn't
Private Sub ReplaceBadEol(currentFileInfo As FileInfo)
Dim fullName As String = currentFileInfo.FullName
If FirstLineEndsWithCrLf(fullName) Then Exit Sub
Dim fileContent As String() = GetFileContent(currentFileInfo.FullName)
Dim pureFileName As String = Path.GetFileNameWithoutExtension(fullName)
Dim newFileName As String = Path.Combine(currentFileInfo.DirectoryName, pureFileName & fileNamePostFix)
File.WriteAllLines(newFileName, fileContent)
currentFileInfo.Delete()
File.Move(newFileName, fullName)
End Sub
'Enum to provide info on the return
Private Enum Terminators
None = 0
CrLf = 1
Lf = 2
Cr = 3
End Enum
'Eol test reads file, advances to the end of the first line and evaluates the value
Private Function GetTerminator(fileName As String, length As Integer) As Terminators
Using sr As New StreamReader(fileName)
sr.BaseStream.Seek(length, SeekOrigin.Begin)
Dim data As Integer = sr.Read()
While data <> -1
If data = 13 Then
data = sr.Read()
If data = 10 Then
Return Terminators.CrLf
End If
Return Terminators.Cr
End If
If data = 10 Then
Return Terminators.Lf
End If
data = sr.Read()
End While
End Using
Return Terminators.None
End Function
'Checks if file is empty, if not check for EOL terminator
Private Function FirstLineEndsWithCrLf(fileName As String) As Boolean
Using reader As New System.IO.StreamReader(fileName)
Dim line As String = reader.ReadLine()
Dim length As Integer = line.Length
Dim fileEmpty As Boolean = String.IsNullOrWhiteSpace(line)
If fileEmpty = True Then
Return True
Else
If GetTerminator(fileName, length) <> 1 Then
Return False
End If
Return True
End If
End Using
End Function
'Reads all lines into String Array
Private Function GetFileContent(fileName As String) As String()
Return File.ReadAllLines(fileName)
End Function
The reason your lines are testing negative for VbCrLf, VbLf and VbCr is because ReadLine strips those. From the StreamReader.ReadLine documentation:
A line is defined as a sequence of characters followed by a line feed ("\n"),
a carriage return ("\r"), or a carriage return immediately followed by a line
feed ("\r\n"). The string that is returned does not contain the terminating
carriage return or line feed.
If you want all the lines, concatenated with a carriage return, try this:
Dim lines As String() = File.ReadAllLines("myfile.txt")
Dim data As String = lines.Aggregate(Function(i, j) i + VbCrLf + j)
This will read in all the lines of your file, then use some Linq to concatenate them all with a carriage return and line feed.
EDIT
If you are looking just to determine what the first line break character is, try this function:
Private Enum Terminators
None = 0
CrLf = 1
Lf = 2
Cr = 3
End Enum
Private Shared Function GetTerminator(fileName As String) As Terminators
Using sr = New StreamReader(fileName)
Dim data As Integer = sr.Read()
While data <> -1
If data = 13 Then
data = sr.Read()
If data = 10 Then
Return Terminators.CrLf
End If
Return Terminators.Cr
End If
If data = 10 Then
Return Terminators.Lf
End If
data = sr.Read()
End While
End Using
Return Terminators.None
End Function
Just call this function, passing in a filename and it will return "Cr", "Lf", "CrLf" or "None" if there are no line terminators.
Related
I have a ListBox with a certain amount of items in it.
For each item in the ListBox a corresponding text file exists in the file directory.
I need to search each text file (based on what's in the ListBox) for a persons name. Each text file may contain the name or it may not.
I would then like a return which text file contains the name.
I have tried this as a way to search a text file: it works, but I'm not sure how to get this to repeat based on whats in a ListBox.
Dim sFileContents As String = String.Empty
If (System.IO.File.Exists((Application.StartupPath) & "\Project_Green.txt")) Then
sFileContents = (System.IO.File.ReadAllText((Application.StartupPath) & "\Project_Green.txt"))
End If
If sFileContents.Contains(TextBox4.Text) Then
MessageBox.Show("yup")
Else
MessageBox.Show("nope")
End If
Also, if it would be possible to ignore case that would be great.
If you have a bunch of files in a directory and you have their names in a ListBox, and you want to search their contents for something.
One liner query:
Imports System.IO
'...
Sub TheCaller()
Dim dir = My.Application.Info.DirectoryPath
Dim ext = ".txt" ' If the extensions are trimmed in the list.
Dim find = TextBox4.Text
Dim files = Directory.EnumerateFiles(dir).Where(Function(x) ListBox1.Items.Cast(Of String).
Any(Function(y) String.Concat(y, ext).
Equals(Path.GetFileName(x),
StringComparison.InvariantCultureIgnoreCase) AndAlso File.ReadLines(x).
Any(Function(z) z.IndexOf(find, StringComparison.InvariantCultureIgnoreCase) >= 0))).ToList
ListBox2.Items.Clear()
ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub
Or if you prefer the For Each loop:
Sub Caller()
Dim dir = My.Application.Info.DirectoryPath
Dim find = TextBox4.Text
Dim files As New List(Of String)
For Each f As String In ListBox1.Items.Cast(Of String).
Select(Function(x) Path.Combine(dir, $"{x}.txt"))
If File.Exists(f) AndAlso
File.ReadLines(f).Any(Function(x) x.IndexOf(find,
StringComparison.InvariantCultureIgnoreCase) <> -1) Then
files.Add(f)
End If
Next
ListBox2.Items.Clear()
ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub
Either way, the files list contains the matches if any.
Plus a pseudo-parallel async method, for the very heavy-duty name searches.
The Async Function SearchNameInTextFiles returns a named Tuple:
(FileName As String, Index As Integer)
where FileName is the file parsed and Index is the position where the first occurrence of the specified name (theName) was found.
If no matching sub-string is found, the Index value is set to -1.
The caseSensitive parameter allows to specify whether the match should be, well, case sensitive.
You can start the search from a Button.Click async handler (or similar), as shown here.
Imports System.IO
Imports System.Threading.Tasks
Private Async Sub btnSearchFiles_Click(sender As Object, e As EventArgs) Handles btnSearchFiles.Click
Dim filesPath = [Your files path]
Dim theName = textBox4.Text ' $" {textBox4.Text} " to match a whole word
Dim ext As String = ".txt" ' Or String.Empty, if extension is already included
Dim tasks = ListBox1.Items.OfType(Of String).
Select(Function(f) SearchNameInTextFiles(Path.Combine(filesPath, f & ext), theName, False)).ToList()
Await Task.WhenAll(tasks)
Dim results = tasks.Where(Function(t) t.Result.Index >= 0).Select(Function(t) t.Result).ToList()
results.ForEach(Sub(r) Console.WriteLine($"File: {r.FileName}, Position: {r.Index}"))
End Sub
Private Async Function SearchNameInTextFiles(filePath As String, nameToSearch As String, caseSensitive As Boolean) As Task(Of (FileName As String, Index As Integer))
If Not File.Exists(filePath) then Return (filePath, -1)
Using reader As StreamReader = New StreamReader(filePath)
Dim line As String = String.Empty
Dim linesLength As Integer = 0
Dim comparison = If(caseSensitive, StringComparison.CurrentCulture,
StringComparison.CurrentCultureIgnoreCase)
While Not reader.EndOfStream
line = Await reader.ReadLineAsync()
Dim position As Integer = line.IndexOf(nameToSearch, comparison)
If position > 0 Then Return (filePath, linesLength + position)
linesLength += line.Length
End While
Return (filePath, -1)
End Using
End Function
You can do these simple steps for your purpose:
First get all text files in the application's startup directory.
Then iterate over all names in the ListBox and for each one, search in all text files to find the file that contains that name.
To make the process case-insensitive, we first convert names and text file's contents to "lower case" and then compare them. Here is the full code:
Private Sub findTextFile()
'1- Get all text files in the directory
Dim myDirInfo As New IO.DirectoryInfo(Application.StartupPath)
Dim allTextFiles As IO.FileInfo() = myDirInfo.GetFiles("*.txt")
'2- Iterate over all names in the ListBox
For Each name As String In ListBox1.Items
'Open text files one-by-one and find the first text file that contains this name
Dim found As Boolean = False 'Changes to true once the name is found in a text file
Dim containingFile As String = ""
For Each file As IO.FileInfo In allTextFiles
If System.IO.File.ReadAllText(file.FullName).ToLower.Contains(name.ToLower) Then 'compares case-insensitive
found = True
containingFile = file.FullName
Exit For
End If
Next
'Found?
If found Then
MsgBox("The name '" + name + "' found in:" + vbNewLine + containingFile)
Else
MsgBox("The name '" + name + "' does not exist in any text file.")
End If
Next
End Sub
I had a look at List.Contains returns false, even though it seems it should return true but his code structure is a bit different to mine so im unsure if I have the same issue.
Before I continue, Let me explain what my result should be.
We have 2 input files, File 1 with email:hash's the other with a mix of email:hash and email:plain.
End output: If the Second file has plaintext after :, Output it (Making sure not to make duplicates when outputting file 1's email:hash's if no file 2 line for that email/hash is made), Otherwise output with the Hash.
tl;dr - Basically make the Second File overwrite prioritized over the First File.
(First File Randomized)
ABC_123#gmail.com:f6deea50e7eeb2d930fab83ccc32cdfe
123abc#domain.ext:82e6eeea4060c90cc3dc6ddd25885806
123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd
abc123#email.com:2d366131008f89781b8379bed3451656
(Second File Randomized)
123abc#domain.ext:aaaaaaaa
ABC_123#gmail.com:cccccccc
abc123#email.com:bbbbbbbb
newemail#hotmail.com:ddddddddd
Output should be:
123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd
123abc#domain.ext:aaaaaaaa
ABC_123#gmail.com:cccccccc
abc123#email.com:bbbbbbbb
newemail#hotmail.com:ddddddddd
(Output from Tests - "->" lines shouldn't be outputted.)
123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd
->123abc#domain.ext:82e6eeea4060c90cc3dc6ddd25885806
123abc#domain.ext:aaaaaaaa
ABC_123#gmail.com:cccccccc
->ABC_123#gmail.com:f6deea50e7eeb2d930fab83ccc32cdfe
abc123#email.com:bbbbbbbb
newemail#hotmail.com:ddddddddd
In the Second OpenFileDialog Block it always returns false until the LAST line in the For Each combo as Match in matches.
Weirdly, If I change the second regex from (.*)#(.*):(.*) to (.*)#(.*):([a-f0-9]{32}) it for some reason works, The issue with that is it will only match Email#domain.ext:{md5} and won't match for example Email#domain.ext:abc123 which is a requirement.
(Latest code update where I was messing around to try fix it broke it even more so this doesn't even work now).
I slept for once and came back on to try fix it, So far im almost there, It's overwriting correctly except for on one email:hash for some reason.
Image showing error
As you can see it changed the 123abc#domain.ext from the hash to aaaaaaa but for the ABC_123#gmail.com it didn't for some strange reason. Also yes the abc123#email.com hash does change so it's odd that a random email:hash didn't change.
I have been at this for about 9 12+ hours straight. (No Exaggeration) and i'd really love an enlightenment on what's going on.
I have tried so many alternatives and such that I can't even remember at this point.
Code: (Updated x3)
Reminder: Read above on what im trying to achieve :)
#Region "Merge Combo's"
Private Sub List_Merge_Click(sender As Object, e As EventArgs) Handles List_Merge.Click
Dim ofd = New OpenFileDialog()
ofd.Title = "Import..."
ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
ofd.Filter = "Text files|*.txt"
ofd.Multiselect = True
'If the user selects 2 Files;
If (ofd.ShowDialog() = DialogResult.OK And ofd.CheckFileExists = True) Then
'Make sure there are no previously stored Conversions;
ActionList2.Items.Clear()
'Variables;
Dim MergedCombos As Integer = 0
Dim TotalCombos As Integer = 0
Try
For Each filename As String In ofd.FileNames
Using sr As New StreamReader(filename)
Dim result = filename.Union(filename, New MailEqualityComparer)
'Get all Matches found from the Regex Condition;
Dim combos As MatchCollection = New Regex("^([^#]+)#(.*):(.*)$", RegexOptions.Multiline).Matches(sr.ReadToEnd)
'Add each Match to the ActionList except for Duplicates;
For Each combo As Match In combos
'Increment the Total Combo's count;
TotalCombos += 1
'If the ActionList doesn't contain the same Combo;
If Not ActionList2.Items.Contains(combo.Value) Then
'If the email is already in the ActionList;
If IsInListbox(ActionList2, combo.Groups(1).Value + "#" + combo.Groups(2).Value) = True Then
'This combo is presumed to be a Hash Decrypted Combo - Overwrite it with the Encrypted Hash;
ActionList2.Items.Add(combo.Value)
'Remove the Hash Item from ActionList;
ActionList2.Items.RemoveAt(FindListboxIndex(ActionList2, combo.Groups(1).Value + "#" + combo.Groups(2).Value))
Else
'Add the Combo;
ActionList2.Items.Add(combo.Value)
End If
End If
Next
End Using
Next
Catch ex As Exception
Console.WriteLine("Error: " + ex.ToString)
Finally
'If atleast 1 Item is in the ActionList, Enable the Export Button;
If ActionList2.Items.Count > 0 Then
ExportButton.Enabled = True
ExportButton.BackColor = Color.FromArgb(149, 255, 141)
End If
'Update the Merged Combo's count;
StatusBar_LeftText.Text = MergedCombos.ToString
'If MergedCombos are less than TotalCombos, Add a "x Duplicates Removed" message;
If MergedCombos < TotalCombos Then
StatusBar_LeftText.Text += " - " + (TotalCombos - MergedCombos).ToString + " Duplicates Removed"
End If
'Autoscroll;
ActionList2.TopIndex = ActionList2.Items.Count - 1
End Try
End If
End Sub
Private Function FindListboxIndex(lb As ListBox, searchString As String) As Integer
For i As Integer = 0 To lb.Items.Count - 1
If lb.Items(i).ToString().Contains(searchString) Then
Return i
Exit Function
End If
Next
Return -1
End Function
Private Function IsInListbox(lb As ListBox, searchString As String) As Boolean
For i As Integer = 0 To lb.Items.Count - 1
If lb.Items(i).ToString().Contains(searchString) Then
Return True
Exit Function
End If
Next
Return False
End Function
#End Region
Hi, I think this should fit to your Needs.
Dim ofd = New OpenFileDialog()
ofd.Title = "Import..."
ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
ofd.Filter = "Text files|*.txt"
ofd.Multiselect = True
Dim output As New Dictionary(Of String, String) 'key = mail, value = plain/hash
If (ofd.ShowDialog() = DialogResult.OK And ofd.CheckFileExists = True) Then
For Each filename As String In ofd.FileNames
Using sr As New StreamReader(filename)
Dim combos As MatchCollection = New Regex("(.*)#(.*):(.*)").Matches(sr.ReadToEnd)
For Each match As Match In combos
Dim tmp() As String = Split(match.ToString, ":")
tmp(1) = tmp(1).Replace(vbCr, "").Replace(vbLf, "") 'Delete carriage return
If Not output.ContainsKey(tmp(0)) Then
output.Add(tmp(0), tmp(1))
Else
If output(tmp(0)).Length = 32 Then 'condition whether to change the value or not. You need to design it to your needs.
output(tmp(0)) = tmp(1)
End If
End If
Next match
End Using
Next filename
End If
I don't know if there is a carriage return in your data. I tried with simple txt files and there was.
You need to change the condition to your needs. I saw the hashes have 32 signs and the plain text does not..
What you want is the union of the two set of lines you have and that's exactly what Enumerable.Union do provided we have a way to distinguish equal elements.
Starting from this we first define that equality comparer :
Class MailEqualityComparer
Implements IEqualityComparer(Of String)
Overloads Function Equals(mail1 As String, mail2 As String) As Boolean Implements IEqualityComparer(Of String).Equals
' assume input validated
Return mail1.Split(":"c)(0).Equals(mail2.Split(":"c)(0))
End Function
Overloads Function GetHashCode(mail As String) As Integer Implements IEqualityComparer(Of String).GetHashCode
' assume input validated
Return mail.Split(":"c)(0).GetHashCode
End Function
End Class
That is for this class two string are equal if their part before the : are equal.
Then you just have to do the Union using an instance of that class to ensure proper equality :
' file1 and file2 are String arrays
' they could be the result of File.ReadAllLines for example
Dim file1 = {
"ABC_123#gmail.com: f6deea50e7eeb2d930fab83ccc32cdfe",
"123abc#domain.ext:82e6eeea4060c90cc3dc6ddd25885806",
"123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd",
"abc123#email.com: 2d366131008f89781b8379bed3451656"
}
Dim file2 = {
"123abc#domain.ext: aaaaaaaa",
"ABC_123#gmail.com: cccccccc",
"abc123#email.com: bbbbbbbb",
"newemail#hotmail.com: ddddddddd"
}
' result is an IEnumerable(Of String)
' add ToArray (for example) if you want to materialize the result
Dim result = file2.Union(file1, New MailEqualityComparer) ' note the order ; it matters
' result content :
' ----------------
' 123abc#domain.ext: aaaaaaaa
' ABC_123#gmail.com: cccccccc
' abc123#email.com: bbbbbbbb
' newemail#hotmail.com: ddddddddd
' 123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd
It just leaves us with "how do we determine the order for Union parameters ?"
For that we have to have a way to know which file contains the "plain text" stuff, information you didn't provided at time of writing.
(Note: the same could have been achieved using Hashset(Of String) or SortedSet(Of String) and their UnionWith method)
[SortedSet requires an IComparer(Of String) instead of an IEqualityComparer(Of String)]
Edit after comment
If I understood correctly your comment (which I'm not sure) ; here is what could be done using a SortedSet :
Class MailComparer
Implements IComparer(Of String)
Function Compare(mail1 As String, mail2 As String) As Integer Implements IComparer(Of String).Compare
' assume input validated
Return mail1.Split(":"c)(0).CompareTo(mail2.Split(":"c)(0))
End Function
End Class
' file1 and file2 are String arrays
' they could be the result of File.ReadAllLines for example
Dim file1 = {
"ABC_123#gmail.com: f6deea50e7eeb2d930fab83ccc32cdfe",
"123abc#domain.ext:82e6eeea4060c90cc3dc6ddd25885806",
"123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd",
"abc123#email.com: 2d366131008f89781b8379bed3451656"
}
Dim file2 = {
"123abc#domain.ext: aaaaaaaa",
"ABC_123#gmail.com: cccccccc",
"abc123#email.com: bbbbbbbb",
"newemail#hotmail.com: ddddddddd"
}
' allLines is an IEnumerable(Of String)
Dim allLines = file2.Concat(file1) ' note the order ; it matters
Dim result As New SortedSet(Of String)(allLines, New MailComparer)
' result content :
' ----------------
' 123_ABC#gmail.com:8fa5104d4d995dc153e5509ab988bcfd
' 123abc#domain.ext: aaaaaaaa
' ABC_123#gmail.com: cccccccc
' abc123#email.com: bbbbbbbb
' newemail#hotmail.com: ddddddddd
I don't see where it's not simple using an 8 lines class ; but maybe I missed the point...
i'm trying to importe a csv file after creating on the same application, the only probleme is that even if a string matchs a line from the file, comparing them on vb.net dosn't give a true boolean i know i didn't make any mistakes because of the line with the commentary 'THIS LINE , i use a pretty small list to do my test and in that loop, there is always one element that matches exactly the string, but comparing them on vb.net dosn't return a true. the result of this is that just after saving the file to my computer while still having it on my listview1 on the application, i load it to the application again and it duplicates everything
Dim open As New OpenFileDialog
open.Filter = "CSV Files (*.csv*)|*.csv"
If open.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim File As String = My.Computer.FileSystem.ReadAllText(open.FileName)
Dim lines() As String = File.Split(Environment.NewLine)
For i = 1 To (lines.Count - 1)
Dim line As String = lines(i)
Dim Columns() As String = line.Split(";")
Dim addLin As New ListViewItem(Columns)
Dim Alr As Boolean = False
Dim st as string=listView1.Items.Item(k).SubItems.Item(0).Text
For k = 0 To (ListView1.Items.Count - 1)
Dim st as string=listView1.Items.Item(k).SubItems.Item(0).Text
MsgBox(Columns(0)& Environment.NewLine & st) 'THIS LINE
If ListView1.Items.Item(k).SubItems.Item(0).Text = Columns(0) Then
Alr = True
MsgBox("true") 'never showsup even when the previous one show the match
End If
next
If Alr = False Then
MsgBox("false") 'the msgbox is only to check
ListView1.Items.Add(addLin)
End If
next
end if
I'm working on a program that is supposed to perform the calculations for the Flesch Readability Index. The program is supposed to read in a text file "Project7.txt", it's then supposed to display the text in a multi-line text box and perform the following calculations:
Count the number of words in the file.
Count the number of syllables in the file.
Count the number of sentences in the file (a sentence can be ended by a ".", "?", "!", or ":"
The program is then supposed to plug the values into the following formula and display the result in a label (label1).
206.835-85.6*(Number of syllables/Number of words) - 1.015*(Number of words/Number of sentences)
Here is the code I have written so far.
Option Strict On
Imports System.IO
Public Class Form1
Private Sub ExitToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExitToolStripMenuItem.Click
Me.Close()
End Sub
Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
Dim open As New OpenFileDialog
open.Filter = "text files |project7.txt|All file |*.*"
open.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
If open.ShowDialog() = Windows.Forms.DialogResult.OK Then
Dim selectedFileName As String = System.IO.Path.GetFileName(open.FileName)
If selectedFileName.ToLower = "project7.txt" Then
Dim doc As String = ""
Dim line As String
Using reader As New StreamReader(open.OpenFile)
While Not reader.EndOfStream
doc += reader.ReadLine
Console.WriteLine(line)
End While
Dim text = File.ReadAllText("Project7.txt")
Dim words = text.Split(" "c)
Dim wordCount = words.Length
Dim separators As Char() = {"."c, "!"c, "?"c, ":"c}
Dim sentences = text.Split(separators, StringSplitOptions.RemoveEmptyEntries)
Dim sentenceCount = sentences.Length
End Using
Else
MessageBox.Show("You cannot use that file!")
End If
End If
End Sub
Function CountSyllables(word As String) As Integer
word = word.ToLower()
Dim dipthongs = {"oo", "ou", "ie", "oi", "ea", "ee", _
"eu", "ai", "ua", "ue", "au", "io"}
For Each dipthong In dipthongs
word = word.Replace(dipthong, dipthong(0))
Next
Dim vowels = "aeiou"
Dim vowelCount = 0
For Each c In word
If vowels.IndexOf(c) >= 0 Then vowelCount += 1
Next
Return vowelCount
End Function
End Class
Any suggestions are appreciated. Thanks in advance for the help.
Is the code always reporting one more sentence than there actually is?
If so take a look at this from the String.Split method MSDN docs:
When the Split function encounters two delimiters in a row, or a
delimiter at the beginning or end of the string, it interprets them as
surrounding an empty string ("")...
I'm sure your last sentence ends with your sentence delimiter so what's happening is your assignment to sentences is getting an extra, empty array element. See for yourself by breakpointing the line after your assignment and hovering your mouse over sentences. Examine the contents of the array.
The fix is to call Split with the option to remove empty array values. To do that though you'll need to call the Split overload that takes an array of Char for the delimiters:
Replace this line:
Dim sentences = text.Split("."c, "!"c, "?"c, ":"c)
With this:
Dim separators As Char() = {"."c, "!"c, "?"c, ":"c}
Dim sentences = text.Split(separators, StringSplitOptions.RemoveEmptyEntries)
And you should be good.
I have the following code;
Public Sub writetofile()
' 1: Append playername
Using writer As StreamWriter = New StreamWriter("highscores.txt", True)
writer.WriteLine(PlayerName)
End Using
' 2: Append score
Using writer As StreamWriter = New StreamWriter("highscores.txt", True)
writer.WriteLine(Score)
End Using
End Sub
What I now want to do is read all the odd lines of the file (the player names) and the even lines into two separate list boxes, how would I go about that??
I need to modify;
Using reader As StreamReader = New StreamReader("file.txt")
' Read one line from file
line = reader.ReadLine
End Using
I have used one of the following solutions but cannot get it working :(
Public Sub readfromfile()
Using reader As New StreamReader("scores.txt", True)
Dim line As Integer = 0
While Not reader.EndOfStream
If line Mod 2 = 0 Then
frmHighScores.lstScore.Items.Add(line)
Else
frmHighScores.lstScore.Items.Add(line)
End If
line += 1
End While
End Using
End Sub
You can use the Mod operator for this:
Using reader As New StreamReader("highscores.txt", True)
Dim line As Integer = 0
Dim text As String
Do
text = reader.ReadLine()
If text = Nothing Then
Exit Do
End If
If line Mod 2 = 0 Then
''# even line
Else
''# odd line
End If
line += 1
Loop
End Using
This approach also works for cases when it's not an even/odd pattern, but another number of repetions. Say you have 3 lines for each player:
player name 1
score 1
avatar url 1
player name 2
score 2
avatar url 2
...
Then you can get this pattern by using Mod with 3
Dim subLine As Integer = line Mod 3
If subLine = 0 Then
''# player name
ElseIf subLine = 1 Then
''# score
Else
''# avatar url
End If
line += 1
If you can reliably expect there to be an even number of lines in the file, then you can simplify this by reading two at a time.
Using reader As StreamReader = New StreamReader("file.txt")
While Not reader.EndOfStream
Dim player as String = reader.ReadLine()
Dim otherInfo as String = reader.ReadLine()
'Do whatever you like with player and otherInfo
End While
End Using
I don't really know the syntax of VB but something like this:
dim odd as boolean = True
Using reader As StreamReader = New StreamReader("file.txt")
line = reader.ReadLine
if odd then
' add to list A
else
' add to list B
end
odd = Not Odd
End Using
Another possibility is to just read the whole file as a block into a single string and the SPLIT the string on vbcrlf
Dim buf = My.Computer.FileSystem.ReadAllText(Filename)
Dim Lines() = Split(Buf, vbcrlf)
Then, lines will contain all the lines from the file, indexed.
So you could step through them to get each player and his other info.
For x = 0 to ubound(Lines)
'do whatever with each line
next
If the file was HUGE, you wouldn't necessarily want to do it this way, but for small files, it's a quick and easy way to handle it.