How to search multiple text files in a directory for a string of text at once - vb.net

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

Related

VB.net file handling progresses to slowly

Hi i have a app that takes a list of files and searches each file for all the images referenced within each file. When the list is finished I sort and remove duplicates from the list then copy each item/image to a new folder. It works, but barely. I takes hours for the copying to occur on as little as 500 files. Doing the copying in windows explorer if faster, and that defeats the purpose of the application.
I don't know how to streamline it better. Your inputs would be greatly appreciated.
'Remove Dupes takes the list of images found in each file and removes any duplicates
Private Sub RemoveDupes(ByRef Items As List(Of String), Optional ByVal NeedSorting As Boolean = False)
statusText = "Removing duplicates from list."
Dim Temp As New List(Of String)
Items.Sort()
'Remove Duplicates
For Each Item As String In Items
'Check if item is in Temp
If Not Temp.Contains(Item) Then
'Add item to list.
Temp.Add(Item)
File.AppendAllText(ListofGraphics, Item & vbNewLine)
End If
Next Item
'Send back new list.
Items = Temp
End Sub
'GetImages does the actual copying of files from the list RemoveDup creates
Public Sub GetImages()
Dim imgLocation = txtSearchICN.Text
' Get the list of file
Dim fileNames As String() = System.IO.Directory.GetFiles(imgLocation)
Dim i As Integer
statusText = "Copying image files."
i = 0
For Each name As String In GraphicList
i = i + 1
' See whether name appears in fileNames.
Dim found As Boolean = False
' Search name in fileNames.
For Each fileName As String In fileNames
' GraphicList consists of filename without extension, so we compare name
' with the filename without its extension.
If Path.GetFileNameWithoutExtension(fileName) = name Then
Dim FileNameOnly = Path.GetFileName(fileName)
' Debug.Print("FileNameOnly: " & FileNameOnly)
Dim copyTo As String
copyTo = createImgFldr & "\" & FileNameOnly
System.IO.File.Copy(fileName, copyTo)
File.AppendAllText(ListofFiles, name & vbNewLine)
'items to write to rich text box in BackgroundWorker1_ProgressChanged
imgFilename = (name) + vbCrLf
ImageCount1 = i
' Set found to True so we do not process name as missing, and exit For. \
found = True
Exit For
Else
File.AppendAllText(MissingFiles, name & vbNewLine)
End If
Next
status = "Copying Graphic Files"
BackgroundWorker1.ReportProgress(100 * i / GraphicList.Count())
Next
End Sub
'BackgroundWorker1_ProgressChanged. gets file counts and writes to labels and rich text box
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'' This event is fired when you call the ReportProgress method from inside your DoWork.
'' Any visual indicators about the progress should go here.
ProgressBar1.Value = e.ProgressPercentage
lblStatus.Text = CType(e.UserState, String)
lblStatus.Text = status & " " & e.ProgressPercentage.ToString & " % Complete "
RichTextBox1.Text &= (fileFilename)
RichTextBox1.Text &= (imgFilename)
txtImgCount.Text = ImageCount1
Label8.Text = statusText
fileCount.Text = fCount
End Sub
I would change something in your code to avoid the constant writing to a file at each loop and the necessity to have two loops nested.
This is a stripped down version of your GetFiles intended to highlight my points:
Public Sub GetImages()
' Two list to collect missing and found files
Dim foundFiles As List(Of String) = New List(Of String)()
Dim notfoundFiles As List(Of String) = New List(Of String)()
Dim fileNames As String() = System.IO.Directory.GetFiles(imgLocation)
' Loop over the filenames retrieved
For Each fileName As String In fileNames
' Check if the files is contained or not in the request list
If GraphicList.Contains(Path.GetFileNameWithoutExtension(fileName)) Then
Dim FileNameOnly = Path.GetFileName(fileName)
Dim copyTo As String
copyTo = createImgFldr & "\" & FileNameOnly
System.IO.File.Copy(fileName, copyTo)
' Do not write to file inside the loop, just add the fact to the list
foundFiles.Add(FileNameOnly)
Else
notfoundFiles.Add(FileNameOnly)
End If
Next
' Write everything outside the loop
File.WriteAllLines(listofFiles, foundFiles)
File.WriteAllLines(MissingFiles, notfoundFiles)
End Sub

exclude header from csv in vb.net

I got a .csv and I want to load it into a datagridview. I have a button called button1 and I got a datagridview called datagridview1. I click the button and it appears... including the header, which I don't want.
Please:
How do I exclude the header from the .csv ?
code:
Imports System.IO
Imports System.Text
Public Class CSV_Reader
Private Sub CSV_Reader_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim filename As String = "C:\Users\Gaius\Desktop\meepmoop.csv"
Dim thereader As New StreamReader(filename, Encoding.Default)
Dim colsexpected As Integer = 7
Dim sline As String = ""
DataGridView1.Rows.Clear()
Do
sline = thereader.ReadLine
If sline Is Nothing Then Exit Do
Dim words() As String = sline.Split(";")
DataGridView1.Rows.Add("")
If words.Length = colsexpected Then
For ix As Integer = 0 To 6
DataGridView1.Rows(DataGridView1.Rows.Count - 2).Cells(ix).Value = words(ix)
Next
Else
DataGridView1.Rows(DataGridView1.Rows.Count - 2).Cells(0).Value = "ERROR"
End If
Loop
thereader.Close()
End Sub
End Class
meepmoop.csv:
alpha;bravo;charlie;delta;echo;foxtrot;golf
1;meep;moop;meep;moop;meep;moop
2;moop;meep;moop;meep;moop;meep
3;meep;moop;meep;moop;meep;moop
4;moop;meep;moop;meep;moop;meep
5;meep;moop;meep;moop;meep;moop
6;moop;meep;moop;meep;moop;meep
7;meep;moop;meep;moop;meep;moop
8;moop;meep;moop;meep;moop;meep
9;meep;moop;meep;moop;meep;moop
10;moop;meep;moop;meep;moop;meep
edit:
[...]
Dim sline As String = ""
DataGridView1.Rows.Clear()
Dim line As String = thereader.ReadLine()
If line Is Nothing Then Return
Do
sline = thereader.ReadLine
[...]
The above addition to the code works but I have no idea why. Nor do I understand why I have to -2 rather than -1. I can't rely on guesswork, I'm expected to one day do this professionally. But I just can't wrap my head around it. Explanation welcome.
edit:
Do
sline = thereader.ReadLine
If sline Is Nothing Then Exit Do
Dim words() As String = sline.Split(";")
If words.Count = 7 Then
DataGridView1.Rows.Add(words(0), words(1), words(2), words(3), words(4), words(5), words(6))
Else
MsgBox("ERROR - There are " & words.Count & " columns in this row and there must be 7!")
End If
Loop
I've shortened the Loop on the advice of a coworker, taking his word on it being 'better this way'.
Another method, using Enumerable.Select() + .Skip()
As noted in Ondřej answer, there's a specific tool for these operations: TextFieldParser
But, if there are no special requirements and the string parsing is straightforward enough, it can be done with the standard tools, as shown in Tim Schmelter answer.
This method enumerates the string arrays returned by the Split() method, and groups them in a list that can be then used in different ways. As a raw text source (as in this case) or as a DataSource.
Dim FileName As String = "C:\Users\Gaius\Desktop\meepmoop.csv"
Dim Delimiter As Char = ";"c
Dim ColsExpected As Integer = 7
If Not File.Exists(FileName) Then Return
Dim Lines As String() = File.ReadAllLines(FileName, Encoding.Default)
Dim StringColumns As List(Of String()) =
Lines.Select(Function(line) Split(line, Delimiter, ColsExpected, CompareMethod.Text)).
Skip(1).ToList()
DataGridView1.Rows.Clear()
'If the DataGridView is empty, add a `[ColsExpected]` number of `Columns`:
DataGridView1.Columns.AddRange(Enumerable.Range(0, ColsExpected).
Select(Function(col) New DataGridViewTextBoxColumn()).ToArray())
StringColumns.Select(Function(row) DataGridView1.Rows.Add(row)).ToList()
If you instead want to include and use the Header because your DataGridView is empty (it has no predefined Columns), you could use the Header line in the .csv file to create the control's Columns:
'Include the header (no .Skip())
Dim StringColumns As List(Of String()) =
Lines.Select(Function(line) Split(line, Delimiter, ColsExpected, CompareMethod.Text)).ToList()
'Insert the columns with the .csv header columns description
DataGridView1.Columns.AddRange(Enumerable.Range(0, ColsExpected).
Select(Function(col, idx) New DataGridViewTextBoxColumn() With {
.HeaderText = StringColumns(0)(idx)
}).ToArray())
'Remove the header line...
StringColumns.RemoveAt(0)
StringColumns.Select(Function(row) DataGridView1.Rows.Add(row)).ToList()
You can skip the header by calling ReadLine twice. Also use the Using-statement:
Using thereader As New StreamReader(filename, Encoding.Default)
Dim colsexpected As Integer = 7
Dim sline As String = ""
Dim line As String = thereader.ReadLine() ' header
if line is Nothing Then Return
Do
sline = thereader.ReadLine()
If sline Is Nothing Then Exit Do
Dim words() As String = sline.Split(";"c)
' ... '
Loop
End Using
You should use VB.NET class that is designed and tested for this purpose. It is Microsoft.VisualBasic.FileIO.TextFieldParser and you can skip header by calling ReadFields() once before you start parsing in loop.

VB.NET - List.Contains Returns False But Should be True

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...

How do I create text file with 2 variable in it?

I have a program that saves a name and score. Currently it stores the name and score in 2 different files. I display them as high scores with 2 listboxes.
I know that it is possible to do something along the lines of ('score|name') and then call it back and split it with the pipe. I want to be able to save a number and name in that sort of format and then call it back in a listbox and have the largest number and the corresponding name show in a label as "Highscore held by {name} with {number}!"
Alternatively, as the project has 3 variables: type, mode and difficulty, along with the name and score. Would it be possible to save it as ('type|mode|diff|score|name') and then check for the correct mode etc. for each page on the high scores page.
I am not wording this too well, so I can upload the entire code to GitHub if need be. I have done this in a very round-about way.
I have posted the relevent code below.
Sub AA1()
Dim FILE_NAME As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "calculator\addition\attack\1.txt")
Dim aryText(0) As String
aryText(0) = mdiparent1.overall
Dim objWriter As New System.IO.StreamWriter(FILE_NAME, True)
objWriter.WriteLine(aryText(0))
objWriter.Close()
MsgBox("Score and Name Saved")
End Sub
\\
Sub AAN1()
Dim FILE_NAME As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "calculator\addition\attack\name\1.txt")
Dim aryText(0) As String
aryText(0) = mdiparent1.username
Dim objWriter As New System.IO.StreamWriter(FILE_NAME, True)
objWriter.WriteLine(aryText(0))
objWriter.Close()
'MsgBox("Score and Name Saved")
End Sub
\\
Sub file_createAA()
Dim filepath As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "calculator\addition\attack\1.txt")
If Not System.IO.File.Exists(filepath) Then
System.IO.File.Create(filepath).Dispose()
End If
End Sub
\\
Sub file_createAAN()
Dim filepath As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "calculator\addition\attack\name\1.txt")
If Not System.IO.File.Exists(filepath) Then
System.IO.File.Create(filepath).Dispose()
End If
End Sub
you can try this (VB.net code now)
Dim name As List(Of String)
Dim score As List(Of Integer)
Dim delimiter As String = "###"
Dim objWriter As New System.IO.StreamWriter(FILE_NAME, True)
For i As Integer = 0 To name.Count() Step 1
objWriter.WriteLine(name(i) + delimiter + score(i).ToString())
Next
objWriter.Close()

Renaming files listed within a listbox

I'm trying to create a little program thats able to alter the names of shotcuts, from a listbox-index.
I've created a button wich list every file with the targeted "extension"(targeted with combobox1), from where I want another button to be able to alter the files names:
Button 1 code(Searching for the files):
Dim kilde As New FolderBrowserDialog
If kilde.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim mappe = New System.IO.DirectoryInfo(kilde.SelectedPath)
txtbSti.Text = kilde.SelectedPath
End If
For Each f In Directory.GetFiles(txtbSti.Text, "*" & ComboBox1.Text & "*", SearchOption.AllDirectories)
If File.Exists(f) Then
With ListBox1
With .Items
.Add(f)
End With
End With
End If
Next f
This gives me a list with the desired files.
Is there a way, to rename the files, in my case,listed within listbox1, line by line?
Button 2 (not functioning)
For Each r As String In ListBox1.Items
System.IO.Path.ChangeExtension(ComboBox1.Text, " ")
Next r
You can use File.Move to "rename" a file:
System.IO.File.Move("oldfilename", "newfilename")
For example:
For Each oldFilePath As String In ListBox1.Items
If System.IO.File.Exists(oldFilePath) Then
Dim dir = System.IO.Path.GetDirectoryName(oldFilePath)
Dim newFilePath = System.IO.Path.Combine( dir, "newFileName")
System.IO.File.Move(oldFilePath, newFilePath)
End If
Next
Edit: In this case, remove the automatic generated extension ".avi - Shortcut"
If you just want to change an extension you can use Path.ChangeExtension:
System.IO.Path.ChangeExtension(oldFilePath, "new_extension")
Update: oh, sorry, it's a part of the name; the file is named, example; J:\Homemovies\Jumping around.avi - Shortcut.lnk, I want to remove the ".avi - Shortcut" part of the name on the actual file(s) listed within the listbox1 that is set up to find all the files within a targeted folder with that particular extension within it's name; J:\Homemovies\Jumping around.lnk
You can use String.Replace:
Dim name = System.IO.Path.GetFileName(oldFilePath).ToLowerInvariant()
Dim newName = name.Replace(".avi - shortcut.", ".")
Dim newPath = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(oldFilePath), newName)
Update2: still can't get either of those solutions to work
Here is a complete working sample:
First, read the shortcuts from your directory:
Dim dir = "C:\Temp\Homemovies"
For Each fn In Directory.EnumerateFileSystemEntries(dir, "*avi - shortcut*.*", SearchOption.AllDirectories)
ListBox1.Items.Add(fn)
Next
Second (in your button-click handler), rename those .Ink files by removing the part that contains(substring)"avi - shortcut", also handling the case that it already exists:
For Each fn As String In ListBox1.Items
If File.Exists(fn) Then
Dim folder = Path.GetDirectoryName(fn)
Dim fileName = Path.GetFileNameWithoutExtension(fn)
Dim extension = Path.GetExtension(fn)
Dim dotParts = fileName.Split("."c)
Dim allButAviShortCut = From part In dotParts
Where part.IndexOf("avi - shortcut", StringComparison.InvariantCultureIgnoreCase) = -1
Dim newFileName = String.Join(".", allButAviShortCut)
Dim newPath = System.IO.Path.Combine(dir, newFileName & extension)
Dim number = 0
While File.Exists(newPath)
' exists already, append number
number += 1
Dim newFileNameWithNum = String.Format("{0}_{1}", newFileName, number)
newPath = System.IO.Path.Combine(dir, newFileNameWithNum & extension)
End While
System.IO.File.Move(fn, newPath)
End If
Next
Consider the following:
ListBox1 contains list of all files in the selected directory.
txtbSti is the text box which holds the path to the selected directory.
Each file is renamed as New_fileX where X is the index of the file in the ListBox1
Now take a look into the code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer = 0
For Each r As String In ListBox1.Items
i += 1
My.Computer.FileSystem.RenameFile(txtbSti.Text & ":\" & r, "New_file" & i & ".txt")
Next r
End Sub