Use .Substring command for an array? - vb.net

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()

Related

for each loop only grabbing the last value of the string array

Note: I'm not posting all code due to it's over 500 lines, I'll show a summary of what I'm trying to accomplish and the issue:
I have a string array that looks like this:
New_BMWM3889;New_LEXIS600;789858;Used_VOL9998
I need to call a routine (the same routine) that will add formatting to the value. I've tried a for each loop, but it's only grabbing the last value of the string array.
I've tried something like this:
dim cars as String = "New_BMWM3889;New_LEXIS600;789858;Used_VOL9998"
dim tmp as String() = cars.Split(";")
dim vin as String
For Each c in tmp
If p.Conatains("New") Then
vin = FormatVin("New", "#", newFormat('0000'))
Else
vin = FormatVin("No Model", "&", newFormat('####'),
End If
Next
so, I have to call the same routine and pass different parameters to the FormatVin routine, however, when I run this I'm only getting the last value of the string array. The formatVin does format validation and will change the format if needed, but that's not the issue, how can I call that same routine but pass different parameters based on if the string in the string array has a prefix or not? Then once the formatting is completed, all of the new formatted values will be passed into a String builder to be used to pass to SQL,
so,
Need to grab all values from the string array
call the routine with the correct parameters based off of the string value.
take all the new formatted strings and passed as one string into a new routine that builds a SQL statement. I know it's a mess, and I'm not sure if it can really be done cleanly if at all. So at the end I should have so I can pass this into my where clause
New_BMWM3889000;New_LEXIS600000;000000789858;Used_VOL9998000
It's hard to say exactly but I think that you should be using something along these lines:
Dim cars = "New_BMWM3889;New_LEXIS600;789858;Used_VOL9998"
Dim tmp = cars.Split(";"c)
For i = 0 To tmp.GetUpperBound(0)
Dim vin = tmp(i)
If vin.Conatains("New") Then
vin = FormatVin("New", vin, newFormat('0000'))
Else
vin = FormatVin("No Model", vin, newFormat('####'),
End If
tmp(i) = vin
Next
cars = String.Join(";", tmp)
It seems like you need to get data out of and into that array. You haven't told us what FormatVin does but I would also assume that you have to pass in the data from the array and get back out a modified version to put back into the array. I might be off on some of the detail, given your vague explanation, but I think this is the basic structure you need.

Streamwriter: write two listboxes on the same row

I am trying to write, in order to export on txt file, information in two listbox with the same number of rows. I have to export them with the following format: Listbox1, Listbox2. In order to do this, I've tried to use the following code:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As Object In Form3.ListBox1.Items And Form3.ListBox2.Items
writer.WriteLine(o)
Next
End Using
I'm receiving the following error:
BC30452 Operator 'And' is not defined for types 'ListBox.ObjectCollection' and 'ListBox.ObjectCollection'.
I've also tried to perform three For Each loops, the first for the LB1, the second for the commas and the third for LB2, but I'm having it exported with content on single lines. How could I solve this?
If you use Enumerable.Zip, as suggested in another answer, then you can make the code more succinct by doing away with the explicit loop:
File.WriteAllLines(SaveFileDialog1.FileName,
Form3.ListBox1.
Items.
Cast(Of Object).
Zip(Form3.ListBox2.
Items.
Cast(Of Object),
Function(x1, x2) $"{x1}, {x2}"))
If you didn't use Zip then you can use a loop this way:
Dim items1 = Form3.ListBox1.Items
Dim items2 = Form3.ListBox2.Items
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For i = 0 To Math.Min(items1.Count, items2.Count)
writer.WriteLine($"{items1(i)}, {items2(i)}")
Next
End Using
The Math.Min part is just in case there are different numbers of items in each ListBox. If you know there aren't then you can do away with that and just use one Count. If there might be different counts but you want to output all items then the code would become slightly more complex to handle that.
As the error message says, the syntax you attempted is simply not valid. There's no feature in VB.NET that does that sort of thing.
However, the .NET Framework API does provide a means for something similar, which would probably work in your case. See Enumerable.Zip(). You can use it like this:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As String In Form3.ListBox1.Items.Cast(Of Object).Zip(Form3.ListBox2.Items.Cast(Of Object), Function(x1, x2) x1 & ", " & x2)
writer.WriteLine(o)
Next
End Using
Since you said that both list boxes have the same number of items we can use the number of items in the first listbox less one (indexes start at zero) in a For loop.
I used a StringBuilder so the code does not have to throw away and create a new string on each iteration.
I used an interpolated string indicate by the $ preceding the string. This means I can insert variables in braces, right along with literals.
Call .ToString on the StringBuilder to write to the text file.
Private Sub SaveListBoxes()
Dim sb As New StringBuilder
For i = 0 To ListBox1.Items.Count - 1
sb.AppendLine($"{ListBox1.Items(i)}, {ListBox2.Items(i)}")
Next
File.WriteAllText("C:\Users\xxx\Desktop\ListBoxText.txt", sb.ToString)
End Sub

VBA check for ENTER character from clipboard

First of all I have little to no knowledge about VBA.. probably none at all. However I was asked to create a VBA program that paste text from clipboard in different cells. My text has the following format:
seminar: name of Seminar (in cell(1,1))
first name: participant's first name (in cell(1,2))
last name: participant's last name (in cell(1,3)) etc..
So far I was able to read the text from clipboard. Then I found the position of the ":" in order to paste only what is AFTER it in the cell.
At this point I thought to find the position of the RETURN character in order to know where the first line ends(ex. "name of Seminar") with this line of code which I found online:
end_str = InStr(str, vbCrLf) - 1
and with the Right (string, length) function to get the relative text.
This is not working. I think because there are not return character in the string variable that holds the data? I don't know.
My question is: Is it possible to check the RETURN character somehow or Is there a better way to create this program?
Thank you in advance.
An easy way would be to use the split function to get each line separately:
Suppose you have a function called ClipBoard_GetData that returns the text from ClipBoard, you could use something like this:
Dim lines() As String
lines = Split(ClipBoard_GetData, vbNewLine)
For Each Line In lines
' Parse each line to get whatever parts you want
Next
This should work fine.. and if you don't -already have a function that gets what's in the clipboard, you could refer to this link
Hope that helps :)
Most likely the Ascii code you're after is 10 (ie newline). So you could find the position of the newline like so:
i = Instr(str, Chr(10))
However, are you aware that you don't need to parse that clipboard text at all. You can write arrays directly into worksheet cells. So all you'd need to do is use the Split function. The procedure below will complete everything you need:
Public Sub PasteText(str As String)
Dim arr() As String
Dim cols As Integer
arr = Split(str, Chr(10))
cols = UBound(arr) + 1
Sheet1.Range("A1").Resize(, cols).Value = arr
End Sub

A Perfectly good Substring Error

Im having a problem parsing a string array of Directories. The end goal is to query the path tied to the [global].MyDataDir & "\saved" to get all folders in this directory. However the actual foldernames, the last bit of text after the last indexof "\" holds the name of a plugin that I need to compare against an enumerated list of plugins for further functionality I won't get into here. The problem here is my last bit of code wont work. The Dim foldername as String = (etc...), It returns an error saying Index and length must refer to a location within the string. Parameter name: length.
Can any of you wizards, help me out here. Much appreciated.
Dim dirList As String() = System.IO.Directory.GetDirectories([global].MyDataDir & "\saved")
For dir As Integer = 0 To dirList.Length - 1
If IO.Directory.GetFiles(dirList(dir)).Length > 0 Then
For Each file As String In IO.Directory.GetFiles(dirList(dir))
Dim folderName As String = dirList(dir).ToString.Substring(dirList(dir).ToString.LastIndexOf("\"), dirList(dir).ToString.Length - 1)
Next
End If
Next
Semper Fi.
Use System.IO.Path.GetDirectoryName() instead.
Next time use the VB.NET Left() convenience function to avoid getting this wrong.
I found the reason....
The problem lies in the arguments of Substring(starting index, length of copy from starting index). I was under the impression, the length argument would take into account the entire string when calculating the length. Instead the second argument of this function acts upon the results of the first argument, not the entire string. So the length of the string is actually much longer than what exists after taking an index of it.
Thanks for the help.

How can I read individual lines of a CSV file into a string array, to then be selectively displayed via combobox input?

I need your help, guys! :|
I've got myself a CSV file with the following contents:
1,The Compact,1.8GHz,1024MB,160GB,440
2,The Medium,2.4GHz,1024MB,180GB,500
3,The Workhorse,2.4GHz,2048MB,220GB,650
It's a list of computer systems, basically, that the user can purchase.
I need to read this file, line-by-line, into an array. Let's call this array csvline().
The first line of the text file would stored in csvline(0). Line two would be stored in csvline(1). And so on. (I've started with zero because that's where VB starts its arrays). A drop-down list would then enable the user to select 1, 2 or 3 (or however many lines/systems are stored in the file). Upon selecting a number - say, 1 - csvline(0) would be displayed inside a textbox (textbox1, let's say). If 2 was selected, csvline(1) would be displayed, and so on.
It's not the formatting I need help with, though; that's the easy part. I just need someone to help teach me how to read a CSV file line-by-line, putting each line into a string array - csvlines(count) - then increment count by one so that the next line is read into another slot.
So far, I've been able to paste the numbers of each system into an combobox:
Using csvfileparser As New Microsoft.VisualBasic.FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvfileparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvfileparser.Delimiters = New String() {","}
While Not csvfileparser.EndOfData
csvalue = csvfileparser.ReadFields()
combobox1.Items.Add(String.Format("{1}{0}", _
Environment.NewLine, _
csvalue(0)))
End While
End Using
But this only selects individual values. I need to figure out how selecting one of these numbers in the combobox can trigger textbox1 to be appended with just that line (I can handle the formatting, using the string.format stuff). If I try to do this using csvalue = csvtranslator.ReadLine , I get the following error message:
"Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
If I then put it as an array, ie: csvalue() = csvtranslator.ReadLine , I then get a different error message:
"Error 1 Number of indices is less than the number of dimensions of the indexed array."
What's the knack, guys? I've spent hours trying to figure this out.
Please go easy on me - and keep any responses ultra-simple for my newbie brain - I'm very new to all this programming malarkey and just starting out! :)
Structure systemstructure
Dim number As Byte
Dim name As String
Dim procspeed As String
Dim ram As String
Dim harddrive As String
Dim price As Integer
End Structure
Private Sub csvmanagement()
Dim systemspecs As New systemstructure
Using csvparser As New FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvparser.Delimiters = New String() {","}
csvalue = csvparser.ReadFields()
systemspecs.number = csvalue(0)
systemspecs.name = csvalue(1)
systemspecs.procspeed = csvalue(2)
systemspecs.ram = csvalue(3)
systemspecs.harddrive = csvalue(4)
systemspecs.optical = csvalue(5)
systemspecs.graphics = csvalue(6)
systemspecs.audio = csvalue(7)
systemspecs.monitor = csvalue(8)
systemspecs.software = csvalue(9)
systemspecs.price = csvalue(10)
While Not csvparser.EndOfData
csvalue = csvparser.ReadFields()
systemlist.Items.Add(systemspecs)
End While
End Using
End Sub
Edit:
Thanks for your help guys, I've managed to solve the problem now.
It was merely a matter calling loops at the right point in time.
I would recommend using FileHelpers to do the reading.
The binding shouldn't be an issue after that.
Here is the Quickstart for Delimited Records:
Dim engine As New FileHelperEngine(GetType( Customer))
// To Read Use:
Dim res As Customer() = DirectCast(engine.ReadFile("FileIn.txt"), Customer())
// To Write Use:
engine.WriteFile("FileOut.txt", res)
When you get the file read, put it into a normal class and just bind to the class or use the list of items you have to do custom stuff with the combobox. Basically, get it out of the file and into a real class asap, then things will be easier.
At least take a look at the library. After using it, we use a lot more simple flat files since it is so easy, and we haven't written a file access routine since (for that kinda stuff).
http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx
I think your main problem is understanding how arrays work (hence the error message).
You can use split and join functions to convert strings into and out of arrays
dim s() as string = split("1,2,3",",") gives and array of strings with 3 elements
dim ss as string = join(s,",") gives you the string back
Firstly, it's actually really good that you are using the TextFieldParser for reading CSV files - most don't but you won't have to worry about extra commas and quoted text etc...
The Readline method only gives you the raw string, hence the "Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
What you may find easier with combo boxes etc is to use an object (e.g. 'systemspecs') rather than strings. Assign the CSV data to the objects and override the "ToString" method of the 'systemspecs' class to display in the combo box how you want with formatting etc. That way when you handle the SelectedIndexChanged event (or similar) you get the "SelectedItem" from the combo box (which can be Nothing so check) and cast it as the 'systemspecs' to use it. The advantage is that you are not restricted to display the exact data in the combo etc.
' in "systemspecs"...
Public Overrides Function ToString() As String
Return Name ' or whatever...
End Function ' ToString
e.g.
dim item as new systemspecs
item.ID = csvalue(1)
item.Name = csvalue(2)
' etc...
combobox1.Items.Add(item)
Let me know if that makes sense!
PK :-)