I have a text file called "Games.txt" that looks like this:
Call of Duty: 50
Assasins Creed: 23
Watch Dogs: 140
If i want to know the number of "Assasins Creed" How would I get that?
What i have tried so far:
I have tried finding it by knowing the line, length of the string. Then reading that and removing the first 15 charachters ("Assasins Creed:") That would leave me with: 23. But this is a pretty bad way of doing it, and it needs me to know the exact line.
What would a better solution be?
If you only want to get the values for "Assasins Creed", with Regex, you can do something like this:
Dim contents As String = IO.File.ReadAllText(filePath)
Dim re As New Regex("(Assasins Creed):\s*(\d+)")
Dim m As Match = re.Match(contents)
If m.Success Then
Console.WriteLine($"The number of '{m.Groups(1).Value}' games is {m.Groups(2).Value}.")
End If
Output:
The number of 'Assasins Creed' games is 23.
If you need to retrieve the values for all games, you can adjust the above code to something like this:
Dim contents As String = IO.File.ReadAllText(filePath)
Dim re As New Regex("(.+):\s*(\d+)")
Dim matches As MatchCollection = re.Matches(contents)
For Each m As Match In matches
Console.WriteLine($"The number of '{m.Groups(1).Value}' games is {m.Groups(2).Value}.")
Next
Output:
The number of 'Call of Duty' games is 50.
The number of 'Assasins Creed' games is 23.
The number of 'Watch Dogs' games is 140.
Notes:
Remember to replace filePath with the actual path of your text file.
You need to add Imports System.Text.RegularExpressions at the top of your class in order to be able to use the Regex class.
Getting started in this game can be tricky because lots of people expect beginners to do their own research and understand it before offering a simple solution and explanation as to why it works. Giving an absolute beginner a regex statement looks more like someone flexing their ego than offering assistance, so if you're looking for a solution that a beginner can not only use, but also have a hope of understanding (and possibly utilise again elsewhere in their code), then the split command could be used like this:
If you Import System.IO it be more useful than regular expressions at this stage I believe
'Place this at the top of the code block above the Class statement
Imports System.IO 'This provides access to File.ReadAllLines (Input/Output)
Then you could create a function which retrieves the information you need and returns the result like this:
''' <summary>
''' Returns the game value plus the index in the list
''' </summary>
''' <param name="FilePath">Full path of the file</param>
''' <param name="Game">Name of the game to return</param>
''' <param name="index">(Optional) Index of the position in the list (-1 if not found)</param>
''' <returns></returns>
Function GameValue(ByVal FilePath As String, ByVal Game As String, Optional ByRef Index As Integer = -1) As Integer
'This returns the file as an array of lines
Dim Lines() As String = File.ReadAllLines(FilePath)
'This loop will iterate through each item in the array
For i As Integer = 0 To Lines.Length - 1
'This turns each line into an array of name and value (either side of the colon + space)
Dim Segments() As String = Lines(i).Split({": "}, StringSplitOptions.None)
'This will bypass any blank lines or lines without ": "
If Segments.Length > 1 Then
'This tries to match the first segment against the name of the game
If Segments(0) = Game Then
'This handles a successful match
'Store the index of the position of the game in the list
Index = i
'Convert final value into Integer and return result
Return Convert.ToInt32(Segments(1))
End If
End If
Next i
'If no correct match for Game is found, 0 is returned
Return 0
End Function
When you want to call the function, you can do this from within a button click for example:
Private Sub cmdApply_Click(sender As Object, e As EventArgs) Handles cmdApply.Click
'Specify the filename however you like
Dim f As String = "C:\ProgramData\game.txt"
'Specify the name of game however you like
Dim g As String = "Watch Dogs"
'Start an index at -1 (This will get modified by the function if the result is found)
Dim i As Integer = -1
'Use the function to return process a result (value)
Dim v As Integer = GameValue(f, g, i)
Console.WriteLine("Game: " & g)
Console.WriteLine("Value: " & v)
Console.WriteLine("Index: " & i)
End Sub
Sometimes, simple things can actually be pretty complicated once you really look into how to do it. And like most things, there's always another way to go about it.
If you try this code you should be able to take out a few things:
How to make and use a function
How to add xml tags to functions to make calling them easier to understand
One method of utilising ByRef to affect the value of a variable declared elsewhere (index)
How to read a text file and process its contents
How to split a string into an array for independent processing
How to convert a string into an integer
Things to note:
An error will occur if file path or name does not exist
An error might occur if Game: xys value is not a number
An error will not occur if the line does not contain ": " because we tested for that
In this case, the game name is case sensitive. You could use Game.ToUpper and Segments(0).ToUpper to convert both values to uppercase when checking if they match if you want to remove case sensitivity
Related
I have a text file which contains the same string of characters in different lines. I read the file using this code:
Dim readTexte() As String = File.ReadAllLines(OuvrirFichier, Encoding.UTF8)
Dim t As String
For Each t In readTexte
If t.Contains(TreeView1.SelectedNode.Text) Then
TextBox2.Text = Trim(t.Substring(0, 18))
TextBox1.Text = Trim(t.Substring(18, 90))
TextBox4.Text = Trim(t.Substring(107, 120))
End If
Next
However, I have a problem because when cutting and reading these strings. The code is not able to choose the right line to match the TreeView node, because it identifies several equally perfect strings.
For example, the first line contains 3 substrings (fixed width fields):
Saint, Augustine, and Doctor of the Church.
The second line contains three sub-strings as well, slightly different:
Saint, Monica, and mother of Saint Augustine
When I want to read, my code gives me two Augustines, and mixes Augustine and Monique! The TextBox2 contains the string contained in the treeview.
How can I fix this?
The treeview is created as simply as possible, thus:
Dim readText () As String = File.ReadAllLines (OpenFile,
Encoding.UTF8)
Dim s As String
For Each s In readText
TextBox2.Text = Trim (s.Substring (0, 18))
TextBox1.Text = Trim (s.Substring (18, 90))
TextBox4.Text = Trim (s.Substring (107, 120))
Dim node As TreeNode = Me.TreeView1.Nodes (0)
TreeView1.Nodes (0) .Nodes.Add (New TreeNode (TextBox1.Text))
Next
We need to know more about how the TreeView is built before we can answer this. It's possible there is simply not enough data associated with the TreeView right now, and the solution will be in a completely different area of the code.
However, I can provide some notes. First, reading a file is one of the slowest things possible to do in a computer. We already see this is small enough to fit in memory; if it's also reasonably stable (doesn't change often), you can save significant work by loading to the array once when the program starts.
Next, I wouldn't keep just a simple array of strings. Instead, I'd parse the data into separate fields right at load. A Tuple, Class, or even string array can all work.
Finally, this code will continue looping even after if finds a match. I'd have a way to stop once we find what we're looking for.
Put it all together like this:
'Create a set of Tuples. Could also use a class here.
Dim readTexte() As IEnumerable(Of (String, String, String)) =
File.ReadLines(OuvrirFichier, Encoding.UTF8).
Select(Function(line) (Trim(line.SubString(0, 18)), Trim(line.SubString(18,90)), Trim(line.SubString(107,120))) )
'Search the collection for the first match
Dim result = readTexte.First(Function(record) TreeView1.SelectedNode.Text.Equals(record.Item1))
TextBox2.Text = result.Item1
TextBox1.Text = result.Item2
TextBox4.Text = result.Item3
Again, this doesn't solve your matching problem, because the question doesn't contain the information we need to help do that. Please edit the question to include more details on how the TreeView is created.
Using VB.NET (VS2013) I'd like to read a delimited file and remove a field if all records have the same blank field (could be multiple fields per record). The delimited file can have "x" number of columns and "y" number of rows and once the blank fields are removed, I need to write it back out as a new delimited file.
The input file will have a header that has to be maintained and the order of the records has to be maintained. I'm familiar with using TextParser to read the file and familiar with writing the file -- what I need help with is reading for a blank field and removing it if it exists across the entire file.
I was thinking I would have to use a datagrid but never used them so looking for some insight to point me in the direction.
Thanks!
I assume each line is delimited with a newline... open as a text file, read a line at a time and use the Split method to break up the line into an array of strings. The split method will take an argument that defines the delimiter you are using. Open the destination file and write the array of strings using a loop.
UPDATE
After thinking about it, you may have records that have a couple of blank columns and the rest have data. I'm not sure if you'd want to remove those columns, because you would lose the structure of the row if you did.
So instead of using StringSplitOptions.RemoveEmptyEntries, you can just do the Split() and then perform some Linq to find out if the Split() produced an array of empty strings.
Something like (with the same results)...
Imports System
Imports System.Linq
Public Module Module1
Public Sub Main()
Dim data As String = "|||||"
Dim pieces As String() = data.Split("|"c)
If pieces.Where(Function(p) String.IsNullOrEmpty(p) = False).Count = 0 Then
Console.WriteLine("All elements are empty")
Else
' Do something
End If
End Sub
End Module
Demo
OLD ANSWER
The String.Split() has an overload that excepts StringSplitOptions. Use StringSplitOptions.RemoveEmptyEntries with your String.Split() and if all records are blank then you'll end up with an array with a length of 0 that you can ignore.
Imports System
Public Module Module1
Public Sub Main()
Dim data As String = "|||||"
Dim pieces As String() = data.Split(new Char() {"|"}, StringSplitOptions.RemoveEmptyEntries)
If pieces.length = 0 Then
Console.WriteLine("All elements are empty")
Else
' Do something
End If
End Sub
End Module
Results:
All elements are empty
Demo
Hey guys I'm trying to make a program that helps people encrypt messages and decrypt messages using the Caesar shift cipher, I know it's probably already been done, I want to have a go myself though.
The problem I've been having is when it comes to encrypting the text. The user selects a number (between 1-25) and then the application will change the letters corresponding to the number chosen, e.g. if the user inputs "HI" and selects 2, both characters are moved two places down the alphabet outputting "JK". My main problem is the replacing characters though, mostly because I've set up the program to be able to encrypt large blocks of text, because my code is:
If cmbxKey.Text = "1" Then
If txtOutput.Text.Contains("a") Then
sOutput = txtOutput.Text.Replace("a", "b")
txtOutput.Text = sOutput
End If
If txtOutput.Text.Contains("b") Then
sOutput = txtOutput.Text.Replace("b", "c")
txtOutput.Text = sOutput
End If
End If
This means if the user inputs "HAY" it will change it to "HBY" and then because of the second if statement it will change it to "HCY" but I only want it to be changed once. Any suggestions to avoid this???? Thanks guys
Since you want to shift all characters, start out by looping though the characters using something like ToArray:
For each s as string in txtOutput.Text.ToArray
'This will be here for each character in the string, even spaces
Next
Then, rather than having cases for every letter, look at it's ascii number:
ACS(s)
...and shift it by the number you want to. Keep in mind that if the number is greater than (I don't know if you want upper/lower case) 122, you want to subtract 65 to get you back to "A".
Then you can convert it back into a character using:
CHR(?)
So this might look something like this:
Dim sb as new text.StringBuilder()
For each s as string in txtOutput.Text.ToArray
If asc(s) > 122 Then
sb.append(CHR(ASC(s) + ?YourShift? - 65)
Else
sb.append(CHR(ASC(s) + ?YourShift?)
END IF
Next
txtOutput.Text = sb.ToString
A very simple method of changing your application while keeping your strategy is to replace the lower case characters with upper case characters. Then they won't be recognized by the Replace method anymore.
Obviously, the problem is that you want to implement an algorithm. In general, an algorithm should be smart in the sense that you don't have to do the grunt work. That's why a method such as the one presented by Steve is smarter; it doesn't require you to map each character separately, which is tedious, and - as most tedious tasks - error prone.
One big issue arise when you're facing a String that the basic Alphanumeric table can't handle. A String that contains words like :
"Déja vu" -> The "é" is going to be what ?
And also, how about encoding the string "I'm Aaron Mbilébé" if you use .ToUpper().
.ToUpper returns "I'M AARON MBILÉBÉ".
You've lost the casing, and how do you handle the shifting of "É" ?
Of course, a code should be smart as pointed above, and I was used to deal with strings just by using the System.Text.ASCIIEncoding to make things easier. But from the moment I started to use large amount of textual datas, sources from the web, files (...) I was forced to dig deeper, and seriously consider string encoding (and System Endianness by the way, when coding and decoding string to/from array of bytes)
Re-think of what do you really want in the end. If you're the only one to use your code, and you're certain that you'll only use A..Z, 0..9, a..z, space and a fixed amount of allowed characters (like puntuation) then, just build a Table containing each of those chars.
Private _AllowedChars As Char() = { "A"c, "B"c, ... "0"c, "1"c, .. "."c, ","c ... }
or
Private _AllowedChars As Char() = "ABCDEF....012...abcd..xyz.;,?:/".ToCharArray()
Then use
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
FinalChars = New Char(AllChars.Length - 1) {} ' It's VB : UpperBound is n+1 item.
' so n items is UpperBound - 1
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) + ShiftValue) Mod _AllowedChars.Length)
Next
Return New String(FinalChars)
End Function
And
Private Function UnShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
' ... the same code until :
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) - ShiftValue + _AllowedChars.Length) Mod _AllowedChars.Length)
' ...
End Function
^^ Assuming ShiftValue is always positive (defined once)
But again, this only works when you have a predefined set of allowed characters. If you want a more flexible tool, you ought to start dealing with encodings, array of byte, BitConverter and have a look at system endianness. That's why I asked if someone else is goind to use your application : let's try this string :
"Xin chào thế giới" ' which is Hello World in vietnamese (Google Trad)
In that case, you may give up..? No ! You ALWAYS have a trick in your cards !
Just create your allowed chars on the fly
Private _AllowedChars As New SortedList(Of Char, Char)
-> get the string to encode (shift)
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
' Build your list of allowed chars...
_AllowedChars.Clear()
For i = 0 To AllChars.Length - 1
If Not _AllowedChars.ContainsKey(AllChars(i)) Then
_AllowedChars.Add(AllChars(i), AllChars(i))
End If
Next
' Then, encode...
FinalChars = New Char(AllChars.Length - 1) {}
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars.Keys.Item((_AllowedChars.IndexOfKey(AllChars(i)) + ShiftValue) Mod _AllowedChars.Count)
Next
Return New String(FinalChars)
End Function
The same for Unshift/decode.
Note : in foreing languages, the resulting string is pure garbage and totally unreadable, unless you (un)shift the chars again.
However, the main limitation of this workaround is the same as the fixed chars array above : Once you encode your string, and add a char in your encoded string that doesn't exists in the initial generated allowed chars, then you've nuked your data and you won't be able to decode your string. All you'll have is pure garbage.
So one day... one day maybe, you'll have to dig deeper at the byte level of the thing, in a defined extended encoding (Unicode/UTF8/16) to secure the integrity of your data.
I'm trying like crazy to figure out how to do this. I need to look at a textfile that I've designed (for a quiz program)- line by line (the line having two parts separated by a comma) for the question (First part) and the boolean answer (second part). Ie. A line from the text file will look like:
You have 10 fingers,true
you have 10 toes,true
you have 2 thumbs,true
I just need to be able to convert this to an array whereby I can access the elements on command, so for instance (0,0) would display the first question in a textbox, and I would reference the users answer (in the form of true or false) against (0,1) where I would use a counter to count the number of correct answers. After the user answers the first question I could loop to the second question to be displayed in the array and so forth. Although there may be more advanced ways of doing this I will need to use a stream reader in this context.
I see I can read ONE line into an array with :
dim line() as string = io.file.readalllines("C:\data.txt")
dim value(2) as integer
value = line.split(","c)
but I need to access each line, with their answers, one at a time. If I could get the textfile into an two dimensional array I could simply access each element on command. Help :)
Any help would be GREATLY appreciated!
I suggest to use a proper class to handle your data. For example you could write a class named QA that stores the Question text and the boolean value of the answer
Public Class QA
Public Question as String
Public Answer as Boolean
End Class
You could read your file with File.ReadLines or use the StreamReader class.
Sub Main
' A list of QA objects loaded from file
Dim qaList as New List(Of QA)()
Using sw = new StreamReader("C:\data.txt")
Dim line as String
Do
line = sw.ReadLine()
If line Is Nothing Then Exit Do
Dim parts() = line.Split(","c)
' Initialize a new QA object and add it to the list
qaList.Add(new QA() _
With
{
.Question = parts(0),
.Answer = Convert.ToBoolean(parts(1))
})
Loop
End Using
Now you could use the List(Of QA) as an array referencing the elements using an indexer
Console.WriteLine(qaList(0).Question)
Console.WriteLine(qaList(0).Answer)
End Sub
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 :-)