I hope someone can help me.
In short, I have this little code
I can't replace "Path from my pc" with a string/textbox in any way
The intention would be this:
enumerator = SpotifyBox.Text.Split.GetEnumerator
It doesn't give me errors, but it doesn't work as it should once the button starts
Dim enumerator As List(Of String).Enumerator = New List(Of String).Enumerator()
Dim class70 As Action(Of String())
ThreadPool.SetMinThreads(40, 40)
Dim strArrays As List(Of String()) = New List(Of String())()
Try
Try
enumerator = File.ReadLines(Path from pc).ToList().GetEnumerator()
While enumerator.MoveNext()
Dim current As String = enumerator.Current
If (If(Not current.Contains(":"), True, String.IsNullOrEmpty(current))) Then
Continue While
End If
strArrays.Add(current.Split(New Char() {":"c}))
End While
Finally
DirectCast(enumerator, IDisposable).Dispose()
End Try
int_5 = strArrays.Count
Catch exception1 As System.Exception
End Try
That is some crazy code and the question you're asking is not the question you need an answer to. Based on what you have posted, it seems that you have a file path in a TextBox named SpotifyBox and you want to read the lines from that file with some processing. In that case, get rid of all that craziness and do this:
Dim filePath = SpotifyBox.Text
Dim records As New List(Of String())
For Each line In File.ReadLines(filePath)
If line.Contains(":") Then
records.Add(line.Split(":"c))
End If
Next
That's it, that's all. You pretty much never need to create an enumerator directly. Just use a For Each loop.
Related
I have a list of string containing full file paths and I'd like to apply a function to each path in that list and get the result in the same or a new list.
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Function FileName(spth As String) As String
'Returns filename with extension from full path
Return System.IO.Path.GetFileName(spth)
End Function
The end result I'd like is for the list Remove to contain the following. I know I could use a loop to do this but I've been learning about lambda expressions lately and feel there should be a simple solution to this.
{"045-0201.iam", "212-D017.ipt", "211-W01.iam"}
Try this
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Remove = Remove.Select(Function(s)
Return IO.Path.GetFileName(s)
End Function).ToList
Calling Select and ToList on the existing List is most likely fine and what most people would do. It's worth being aware, though, that that will not modify the existing collection but rather return a new one. If you only have the one reference to that list then that's not a big deal but other references to the existing list will not see the change, e.g.
Dim fileNames As New List(Of String) From {"C:\Folder\File1.ext",
"C:\Folder\File2.ext",
"C:\Folder\File3.ext"}
Dim temp = fileNames
fileNames = fileNames.Select(Function(s) Path.GetFileName(s)).ToList()
For Each fileName In fileNames
Console.WriteLine(fileName)
Next
For Each fileName In temp
Console.WriteLine(fileName)
Next
If you run that then you'll see that the first loop displays just the files names but the second loop displays the full paths, because it still refers to the original list.
If that's a problem, there is another way to do this without an explicit loop:
Dim fileNames As New List(Of String) From {"C:\Folder\File1.ext",
"C:\Folder\File2.ext",
"C:\Folder\File3.ext"}
Dim temp = fileNames
Array.ForEach(Enumerable.Range(0, fileNames.Count).ToArray(),
Sub(i) fileNames(i) = Path.GetFileName(fileNames(i)))
For Each fileName In fileNames
Console.WriteLine(fileName)
Next
For Each fileName In temp
Console.WriteLine(fileName)
Next
If you run that then you'll see that both loops display just the file names because there's only one list.
That said, if the first code posed a problem because of multiple references to the list, I'd just use a loop.
I know you stated that you'd want something other than a loop, but there really is no needfor anything fancy here. By the way, writing Remove.Add sounds like a riddle.
Sub Main()
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Console.WriteLine("Before execution")
For Each s As String In Remove
Console.WriteLine(s)
Next
For i As Integer = 0 To Remove.Count - 1
Remove(i) = MyFunction(Remove(i))
Next
Console.WriteLine("After execution")
For Each s As String In Remove
Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
Private Function MyFunction(path As String) As String
Return IO.Path.GetFileName(path)
End Function
This outputs:
I am trying to execute a search on a folder and get an array of every result back. I found this code but it doesn't go into subfolders:
Dim Results As New List(Of String)
For Each strFileName As String In IO.Directory.GetFiles("pathToSearch")
If strFileName.Contains("searchTerm") Then
Results.Add(strFileName)
End If
Next
How can I do exactly this, but also look into the subfolders?
I'm not very knowledgeable about the search options in VB.NET yet, so I apologize in advance if this seems stupid. I have tried searching online but haven't found anything. I can't have a single string, it needs to be an array (this needs to be interpreted by the machine later in the program)
Thanks for any help
No recursion required. There is already an overload for this. You just need to call it with appropriate search option.
e.g. To list all txt files in the directory as well as the subdirectories you can do:
Dim foundFiles() As String = System.IO.Directory.GetFiles("path/to/dir", "*.txt", System.IO.SearchOption.AllDirectories)
In order to get the subfolders you might try some recursive function
Unless there's a file system search API with which I'm unfamiliar, this is going to involve a recursive method to perform the searching into sub-directories.
Helpfully, Microsoft even has a complete example of something very similar available. In VB it might look something like this (my VB is very rusty and this is free-hand code, by the way...):
Function FindFiles(ByVal dir As String, ByVal searchTerm As String) As List(Of String)
Dim Results As New List(Of String)
' search files in this directory
For Each strFileName As String In IO.Directory.GetFiles(dir)
If strFileName.Contains(searchTerm) Then
Results.Add(strFileName)
End If
Next
' recurse into child directories
For Each strDirectoryName As String In IO.Directory.GetDirectories(dir)
Results = Results.AddRange(FindFiles(strDirectoryName, searchTerm)
Next
Return Results
End Function
Make a recursive function that keeps calling itself until all subdirectories are checked:
Private Function GetAllFileNamesFromDirectory(ByVal strPath As String, ByVal strSearchTerm As String) As List(Of String)
Dim lstFileNames As New List(Of String)
Dim lstSubDirectories As List(Of String) = IO.Directory.GetDirectories(strPath).ToList()
Dim lstFilesToAdd As List(Of String) = IO.Directory.GetFiles(strPath).ToList()
For Each strFileToAdd As String In lstFilesToAdd
If strFileToAdd.Contains(strSearchTerm) Then
'Additional logic would be required to filter out the directory name.
lstFileNames.Add(strFileToAdd)
End If
Next
If lstSubDirectories.Count > 0 Then
lstSubDirectories.ForEach(Sub(strDirectoryPath As String)
Dim lstSubDirectoryFilesToAdd As List(Of String) = GetAllFileNamesFromDirectory(strDirectoryPath, strSearchTerm)
If lstSubDirectoryFilesToAdd.Count > 0 Then
lstFileNames.AddRange(lstSubDirectoryFilesToAdd)
End If
End Sub)
End If
Return lstFileNames
End Function
I'm working on a Timer program, that allows the user to set up a timer for each individual user account on the computer. I'm having some trouble writing the settings to a text file and reading from it. I want to know if it's possible to write it in this fashion --> username; allowedTime; lastedLoggedIn; remainingTime; <-- in one line for each user, and how would I go about doing that? I also wanted to know if it's possible to alter the text file in this way, in the case that there's already an entry for a user, only change the allowedTime, or the remainingTime, kinda just updating the file?
Also I'm also having trouble being able to read from the text file. First of all I can't figure out how to determine if a selected user is in the file or not. Form there, if the user is listed in the file, how can access the rest of the line, like only get the allowedTime of that user, or the remaining time?
I tried a couple of ways, but i just can't get it to do how I'm imaging it, if that makes sense.
here's the code so far:
Public Sub saveUserSetting(ByVal time As Integer)
Dim hash As HashSet(Of String) = New HashSet(Of String)(File.ReadAllLines("Settings.txt"))
Using w As StreamWriter = File.AppendText("Settings.txt")
If Not hash.Contains(selectedUserName.ToString()) Then
w.Write(selectedUserName + "; ")
w.Write(CStr(time) + "; ")
w.WriteLine(DateTime.Now.ToLongDateString() + "; ")
Else
w.Write(CStr(time) + "; ")
w.WriteLine(DateTime.Now.ToLongDateString() + "; ")
End If
End Using
End Sub
Public Sub readUserSettings()
Dim currentUser As String = GetUserName()
Dim r As List(Of String) = New List(Of String)(System.IO.File.ReadLines("Settings.txt"))
'For Each i = 0 to r.lenght - 1
'Next
'check to see if the current user is in the file
MessageBox.Show(r(0).ToString())
If r.Contains(selectedUserName) Then
MessageBox.Show(selectedUserName + " is in the file.")
'Dim allowedTime As Integer
Else
MessageBox.Show("the user is not in the file.")
End If
'if the name is in the file then
'get the allowed time and the date
'if the date is older than the current date return the allowed time
'if the date = the current date then check thhe remaning time and return that
'if the date is ahead of the current date return the reamining and display a messgae that the current date needs to be updated.
End Sub
edit: I just wanted to make sure if I'm doing the serialization right and the same for the deserialization.
this is what i got so far:
Friend userList As New List(Of Users)
Public Sub saveUserSetting()
Using fs As New System.IO.FileStream("Settings.xml", IO.FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
bf.Serialize(fs, userList)
End Using
End Sub
Public Sub readUserSettings()
Dim currentUser As String = GetUserName()
Dim useList As New List(Of Users)
Using fs As New System.IO.FileStream("Settings.xml", IO.FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
useList = bf.Deserialize(fs)
End Using
MessageBox.Show(useList(0).ToString)
End Sub
<Serializable>
Class Users
Public userName As String
Public Property allowedTime As Integer
Public Property lastLoggedInDate As String
Public Property remainingTime As Integer
Public Overrides Function ToString() As String
Return String.Format("{0} ({1}, {2}, {3})", userName, allowedTime, lastLoggedInDate, remainingTime)
End Function
End Class
edit 2:
I'm not too familiar with try/catch but would this work instead?
Public Sub readUserSettings()
If System.IO.File.Exists("Settings") Then
Using fs As New System.IO.FileStream("Settings", FileMode.Open, FileAccess.Read)
Dim bf As New BinaryFormatter
userList = bf.Deserialize(fs)
End Using
Else
MessageBox.Show("The setting file doesn't exists.")
End If
End Sub
You have a few typos and such in your code, but it is pretty close for your first try:
Friend userList As New List(Of Users)
Public Sub saveUserSetting()
' NOTE: Using the BINARY formatter will write a binary file, not XML
Using fs As New System.IO.FileStream("Settings.bin", IO.FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
bf.Serialize(fs, userList)
End Using
End Sub
Public Sub readUserSettings()
' this doesnt seem needed:
Dim currentUser As String = GetUserName()
' do not want the following line - it will create a NEW
' useRlist which exists only in this procedure
' you probably want to deserialize to the useRlist
' declared at the module/class level
' Dim useList As New List(Of Users)
' a) Check if the filename exists and just exit with an empty
' useRlist if not (like for the first time run).
' b) filemode wass wrong - never create here, just read
Using fs As New System.IO.FileStream("Settings.bin",
FileMode.Open, FileAccess.Read)
Dim bf As New BinaryFormatter
' user list is declared above as useRList, no useList
useList = bf.Deserialize(fs)
End Using
' Console.WriteLine is much better for this
MessageBox.Show(useList(0).ToString)
End Sub
<Serializable>
Class Users
' I would make this a property also
Public userName As String
Public Property allowedTime As Integer
Public Property lastLoggedInDate As String
Public Property remainingTime As Integer
Public Overrides Function ToString() As String
Return String.Format("{0} ({1}, {2}, {3})", userName, allowedTime, lastLoggedInDate, remainingTime)
End Function
End Class
ToDo:
a) decide whether you want XML or binary saves. With XML, users can read/edit the file.
b) Use a file path created from Environment.GetFolder(); with a string literal it may end up in 'Program Files' when deployed, and you cannot write there.
c) when reading/loading the useRlist, use something like
FileStream(myUserFile, FileMode.Open, FileAccess.Read)
It wont exist the first time run, so check if it does and just leave the list empty. After that, you just need to open it for reading. For saving use something like:
FileStream(myUserFile, FileMode.OpenOrCreate, FileAccess.Write)
You want to create it and write to it. You might put the Load/Save code inside a Try/Catch so if there are file access issues you can trap and report them, and so you know the list did not get saved or read.
Using a serializer, the entire contents of the list - no matter how long - will get saved with those 3-4 lines of code, and the entire list read back in the 2-3 lines to load/read the file.
I don't have the answer to all your questions however I've been also working on a timer application and just recently started using text file to read and write information. The method I'm using has proven itself fairly easy to use and not very confusing. Here is an extract of my code:
Dim startup As String = "C:\Users\DigiParent\Desktop\Project data\Digitimeinfo.txt"
Dim reader As New System.IO.StreamReader(startup, Encoding.Default)
Dim data As String = reader.ReadToEnd
Dim aryTextFile(6) As String
aryTextFile = data.Split(",")
This will read everything in the text file and in sort separate everything in between the , and store them individual in the array. To put the code back in one line use
Dim LineOfText As String
LineOfText = String.Join(",", aryTextFile)
so you could write someting like this to write your info to a text file:
Dim startup As String = "C:\Users\DigiParent\Desktop\Project data\Digitimeinfo.txt"
Dim objWriter As New System.IO.StreamWriter(startup, False)
Dim aryTextFile(2) As String
aryTextFile(0) = pasword
aryTextFile(1) = user
aryTextFile(2) = remainingtime
LineOfText = String.Join(",", aryTextFile)
objWriter.WriteLine(LineOfText)
objWriter.Close()
and to read it you could use steam reader.
For some reason, it seems that the outer block doesn't seem to update recursively, as I expected it to. I want the loops to add all directories within "C:\Users\Drise"to the array internaldirs(). Any advice on the correct way to do this, as it seems I'm doing it improperly?
Static internaldirs() As String
internaldirs.add("C:\Users\Drise")
For Each internaldir As String In internaldirs
For Each direc As String In Directory.GetDirectories(internaldir)
internaldirs.Add(direc)
Next
Next
Solution:
Sub recursivedirs()
Static internaldirs As New List(Of String)
Try
If internaldirs(0) = "C:\Users\Drise" Then
Call AddDirToList(internaldirs, internaldirs(0))
End If
Catch
internaldirs.Add("C:\Users\Drise")
Call AddDirToList(internaldirs, internaldirs(0))
End Try
End Sub
Private Sub AddDirToList(ByRef dirs As List(Of String), ByVal currentDir As String)
dirs.Add(currentDir)
Try
For Each subDir As String In Directory.GetDirectories(currentDir)
AddDirToList(dirs, subDir)
Next
Catch
End Try
Short answer is: you can't modify a collection (internaldirs) that you're iterating over.
Longer answer: Looks like you're trying to build a string array listing the folder in the directory tree. A better way would be to use a List and a recursive function.
Static dirs As List(Of String)
dirs = New List(Of String)
AddDirToList(dirs, "C:\Users\Drise")
Private Sub AddDirToList (dirs as List(Of String), currentDir as String)
dirs.Add(currentDir)
For Each subDir As String In Directory.GetDirectories(currentDir)
AddDirToList(dirs, currentDir)
Next
End Sub
Please excuse any syntax issues. I'm more of a C# guy.
As Andrew Cooper said, you can't modify a collection being used in a For Each loop.
You can do it with an index counter.
Dim internaldir As List(Of String)
internaldir.Add("C:\Users\Drise")
Dim i As Integer = 0
Do Until i >= internaldir.Count
Dim internaldir As String = internaldirs(i)
For Each currentdir As String In Directory.GetDirectories(internaldir)
internaldirs.Add(currentdir)
Next
i += 1
Loop
' If you want an array as output, use:
Dim array As String() = internaldirs.ToArray()
I'm trying to modify a program where there is a variable that stores all the specified file types in a String() variable. What I would like to do is to somehow append to this variable in any way if I want to search another directory or just grab another individual file. Any suggestions would be greatly appreciated.
//Grab files from a directory with the *.txt or *.log as specified in the Combo Box
Dim strFiles As String()
strFiles = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories)
EDIT: Edited to include code snippet used.
Dim strFiles As String()
Dim listFiles As List(Of String)(strFiles)
If (cmbtype.SelectedItem = "All") Then
//Do stuff
For index As Integer = 1 To cmbtype.Items.Count - 1
Dim strFileTypes As String() = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.Items(index), IO.SearchOption.AllDirectories)
Next
//Exit Sub
Else
listFiles.Add(System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories).ToString())
End If
Right now you're using a String() which is an array of String instances. Arrays are not well suited for dynamically growing structures. A much better type is List(Of String). It is used in very similar manners to a String() but has a handy Add and AddRange method for appending data to the end.
Dim strFiles As New List(Of String)()
strFiles.AddRange(System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, I
O.SearchOption.AllDirectories)
dim listFiles as list(of string)
listFiles = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories).ToList()
listFiles.Add("..\blah\...\")