visual basic search text for string, display results with propercase - vb.net

...databox.text (from example code below) contains a large list of combined words(domain names) previously populated in the program. There is 1 per each line. In this example, it initially looks like:
thepeople.com
truehistory.com
workhorse.com
whatever.com
neverchange.com
...
The following code below saves the text inside databox to tlistfiltered.txt and then searches tlistfiltered.txt to retrieve all lines that contain any of the items in the list "arr()", and then populates listview(lv) with the results. This works just fine, but the results look like:
thepeople.com
truehistory.com
neverchange.com
...
but what I need is the "found string" (from arr()list to be Proper case so the result would be:
thePeople.com
trueHistory.com
neverChange.com
Here is the code....
Dim s As String = databox.Text
File.WriteAllText(dloc & "tlistfiltered.txt", s)
databox.Clear()
Dim text2() As String = System.IO.File.ReadAllLines(dloc & "tlistfiltered.txt")
Dim arr() As String = {"people", "history", "change"}
For index1 = 0 To arr.GetUpperBound(0)
Dim YesLines() As String = Array.FindAll(text2, Function(str As String)
Return str.Contains(arr(index1))
End Function).ToArray
databox.Visible = True
For index2 = 0 To YesLines.GetUpperBound(0)
Dim match As String = (YesLines(index2)) & vbCrLf
databox.AppendText(match)
Next
Next
s = databox.Text
File.WriteAllText(dloc & "tlistfilteredfinal.txt", s)
databox.Clear()
domains = (From line In File.ReadAllLines(dloc & "tlistfilteredfinal.txt") Select New ListViewItem(line.Split)).ToArray
lv.Items.Clear()
My.Computer.FileSystem.DeleteFile(dloc & "tlistfiltered.txt")
My.Computer.FileSystem.DeleteFile(dloc & "tlistfilteredfinal.txt")
BackgroundWorker1.RunWorkerAsync()
End Sub
Is there a way to do this on the fly? I have tried StrConv etc, but it will only convert the entire line to proper case. I only want the "found" word contained within the line to be converted....
edit:
after seeing #soohoonigan 's answer, i edited
databox.Visible = True
For index2 = 0 To YesLines.GetUpperBound(0)
Dim match As String = (YesLines(index2)) & vbCrLf
databox.AppendText(match)
Next
Next
to this:
databox.Visible = True
For index2 = 0 To YesLines.GetUpperBound(0)
Dim match As String = (YesLines(index2)) & vbCrLf
Dim myTI As System.Globalization.TextInfo = New System.Globalization.CultureInfo("en-US", False).TextInfo
If match.Contains(arr(index1)) Then
match = match.Replace(arr(index1), myTI.ToTitleCase(arr(index1)))
'StrConv(match, vbProperCase)
databox.AppendText(match)
End If
Next
and got the desired result!

Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim test As String = "thepeople.com"
Dim search As String = "people"
Dim myTI As System.Globalization.TextInfo = New System.Globalization.CultureInfo("en-US", False).TextInfo
If test.Contains(search) Then
test = test.Replace(search, myTI.ToTitleCase(search))
MsgBox(test)
End If
Me.Close()
End Sub
End Class

I'm not sure to understand the need for using files for intermediate steps and deleting them at the end for example.
First step: getting the lines of the input
That can be done by using the Lines property of databox (which I suspect to be a TextBox or RichTextBox ; if it's not the case we can still use a Split on the Text property)
Dim lines = databox.Lines ' or databox.Text.Split({Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
Second step: we want to filter those lines to keep only the ones containing the searched texts
For this there are several way, a simple one would be to use a Linq query to get the job done
Third step: transforming the result of the filter replacing the searched text by it's capitalized form
So we continue the started query and add a projection (or mapping) to do the transformation.
We need to use TextInfo.ToTitleCase for that.
' See soohoonigan answer if you need a different culture than the current one
Dim textInfo = System.Globalization.CultureInfo.CurrentCulture.TextInfo
Dim query = From word in arr
From line in lines
Where line.Contains(word)
Let transformed = line.Replace(word, textInfo.ToTitleCase(word))
select transformed
' We could omit the Let and do the Replace directly in the Select Clause

Related

How do I remove multiple empty lines in a text file

I wonder if someone is able to help. I have a m3u file with multiple lines of formatted text.
#EXTM3U
#RADIOBROWSERUUID:963194ef-0601-11e8-ae97-52543be04c81
#EXTINF:1,80s80s Christmas
http://streams.80s80s.de/christmas/mp3-192/streams.80s80s.de/
#RADIOBROWSERUUID:9638cfa5-0601-11e8-ae97-52543be04c81
#EXTINF:1,181.FM - Christmas Kountry
http://www.181.fm/stream/pls/181-xkkountry.pls
Whilst I have managed to extract the data into a format that I can need... I am left with multiple blank lines. A sample bit of code I used to Extract the data is...
If line.StartsWith("#EXTM3U") Then 'Remove
lines(i) = ""
End If
If line.StartsWith("#RADIOBROWSERUUID:") Then 'Remove
lines(i) = ""
End If
If line.StartsWith("#EXTINF:1,") Then 'Add # at beginning of line
lines(i) = line.Replace("#EXTINF:1,", "#")
End If
Which then leaves me with the following...
#80s80s Christmas
#http://streams.80s80s.de/christmas/mp3-192/streams.80s80s.de/
#181.FM - Christmas Kountry
#http://www.181.fm/stream/pls/181-xkkountry.pls
I just dont seem to be able to remove the empty/blank lines. I have used google as well as here and non of the answers seem to work for me. Here is the code that I am using...
Dim Newlines As String() = File.ReadAllLines(ofd.FileName)
For t As Integer = 0 To Newlines.Length - 1
Dim line2 As String = Newlines(t)
If line2.StartsWith("") Then ' Remove blank lines
Beep()
Newlines(t) = line2.Replace(Environment.NewLine, String.Empty)
End If
Next
File.WriteAllLines("NewTextm3u.txt", lines)
Can any body see where I am going wrong? Thank you very much.
You can do this:
Dim sFile As String = "c:\test2\test2.txt"
Dim Fdata As New List(Of String)
Fdata = File.ReadAllLines("c:\test2\test.txt").ToList
For i = Fdata.Count - 1 To 0 Step -1
If Fdata(i) = "" Then
Fdata.RemoveAt(i)
End If
Next
For Each sLine As String In Fdata
Debug.Print(sLine)
Next
File.WriteAllLines(sFile)
The above would remove all blank lines
In place of that loop, you could also use lamda expression like this:
Fdata.RemoveAll(Function(MyOneRow) MyOneRow = "")
I used the StrignSplitOptions.RemoveEmptyEntries to get rid of blank lines.
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim FileContents = File.ReadAllText("SomeFile.txt")
Dim lines = FileContents.Split(Environment.NewLine.ToCharArray, StringSplitOptions.RemoveEmptyEntries)
End Sub
As you can see in the above image, there is what we can call an "enter" at the end of the sentence and another one between.
So let's just remove the one between the sentences and because it's just a line you can do it like this:
If line(i) = CHR(13) & CHR(10) then line(i)=""
but if you want to get a little paranoid and just want to remove all the "enters" or "line breaks" just do it like this:
line(i)=Replace(line(i),CHR(13) & CHR(10),"")

VB ListBox list ftp directories and remove multiple lines based on number in folder name

I do get a lot of help from this website searching for a solution but at this point I'm completely stuck. I'm not a programmer, still was able to get that far.
The idea of my little project is to download newest version of a program (folder) from FTP server. Unzip files and update current program folder with new files. But before that backup the exiting program folder to a different FTP address.
What I'm struggling with is a way they post program versions (folders) on FTP:
folder structure on FTP
I can't really predict what would be the next folder to download because e.g. for program version:
7.6.16
it might be:
WERSJA_NOWY_TEMPLATE_7.6.17
or
WERSJA_NOWY_TEMPLATE_7.7.01
what ever is being released.
There is an ini file on C drive that holds the current program version. I was able to retrieve that information by reading the line, removing the unnecessary characters and splitting the string:
Dim wersja1 As String = File.ReadAllLines("C:\Windows\file.ini").FirstOrDefault(Function(x) x.Contains("WERSJA="))
Dim characterToRemove1 As String = "WERSJA="
Dim wersja2 As String = Replace(wersja1, characterToRemove1, "")
Dim characterToRemove2 As String = "T"
Dim wersja3 As String = Replace(wersja2, characterToRemove2, "")
Dim wersja4 As String() = wersja3.Split(New Char() {"."c})
Dim w1 As String = wersja4(0)
Dim w2 As String = wersja4(1)
Dim w3 As String = wersja4(2)
e.g. from a line:
WERSJA=7.6.5T
I get:
w1 = 7
w2 = 6
w3 = 5
Then I change the last one (w3) to a two digits number (because of the way folders are named on FTP):
If w3 < 10 Then
w3 = "0" & w3
Else
w3 = w3
End If
So if:
w3 = 5 >> I get 05
w3 = 16 >> I get 16
So far, so good.
In the next step I would like to see a filtered list of program versions (folders) in a ListBox or 2 ListBoxes. I would like to filter the list to see only folder with a bigger number than the current program version, so I can choose the correct one to download.
Why this way? Because if current version is e.g.
7.6.16
and there are 3 new ones e.g.:
7.6.17
7.6.18
7.7.01
I need to download all of them one by one, update program files and start the program to update sql database in order they were released. I can't skip any version on update.
Dim FTPurl As String = "ftp://xx.xx.xxx.xxx/wersje/"
Dim FTPwersja As String = "WERSJA_NOWY_TEMPLATE_*"
Dim FTPlog As String = "xxx"
Dim FTPpass As String = "yyy"
Dim clsRequest As FtpWebRequest = System.Net.WebRequest.Create(FTPurl & FTPwersja)
clsRequest.Credentials = New System.Net.NetworkCredential(FTPlog, FTPpass)
clsRequest.Method = System.Net.WebRequestMethods.Ftp.ListDirectory
Dim listResponse As FtpWebResponse = clsRequest.GetResponse
Dim reader As StreamReader = New StreamReader(listResponse.GetResponseStream())
'folder list
While reader.Peek >= 0
ListBox1.Items.Add(reader.ReadLine)
End While
'remove empty lines
For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
If ListBox1.GetItemText(ListBox1.Items(i)) = String.Empty Then
ListBox1.Items.RemoveAt(i)
End If
Next i
The above code gives me this result.
I found this code that enables filtering of ListBox but I have no clue on how to convert it to an loop so I would see only folders with bigger numbers than the current version of program:
'filter result in list box
Dim items = From it In ListBox1.Items.Cast(Of Object)()
Where it.ToString().IndexOf("7.5", StringComparison.CurrentCultureIgnoreCase) >= 0
Dim matchingItemList As List(Of Object) = items.ToList()
ListBox1.BeginUpdate()
'ListBox1.Items.Clear() 'add to list
For Each item In matchingItemList
ListBox1.Items.Remove(item) 'remove from list
'ListBox1.Items.Add(item) 'add to list
Next
ListBox1.EndUpdate()
I also have this code to catch choice and prevent crash when clicked on empty line :
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) _
Handles ListBox1.SelectedIndexChanged
Try
Dim LB1choice = ListBox1.SelectedItem.ToString()
Label5.Text = LB1choice
Catch ex As Exception
ListBox1.SelectedIndex = 0
End Try
End Sub
The ideal would be 2 ComboBoxes where 1st would display e.g.
WERSJA_NOWY_TEMPLATE_7.6
WERSJA_NOWY_TEMPLATE_7.7
and based on the choice 2nd ComboBox would list newer subfolders in it e.g.
WERSJA_NOWY_TEMPLATE_7.6.16
WERSJA_NOWY_TEMPLATE_7.6.17
for the 1st one, and:
WERSJA_NOWY_TEMPLATE_7.7.01
for the 2nd one.
I much appreciate if anyone could help me this because it's beyond my skills.

How can i check for a character after certain text within a listbox?

How can i check for a character after other text within a listbox?
e.g
Listbox contents:
Key1: V
Key2: F
Key3: S
Key4: H
How do I find what comes after Key1-4:?
Key1-4 will always be the same however what comes after that will be user defined.
I figured out how to save checkboxes as theres only 2 values to choose from, although user defined textboxes is what im struggling with. (I have searched for solutions but none seemed to work for me)
Usage:
Form1_Load
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
Which textbox1.text would then contain V / whatever the user defined.
I did try something that kind of worked:
Form1_Load
Dim UsersKey as string = "V"
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
but i'm not sure how to add additional letters / numbers to "V", then output that specific number/letter to the textbox. (I have special characters blocked)
Reasoning I need this is because I have created a custom save settings which saves on exit and loads with form1 as the built in save settings doesn't have much customization.
e.g Can't choose save path, when filename is changed a new user.config is generated along with old settings lost.
Look at regular expressions for this.
Using the keys from your sample:
Dim keys As String = "VFSH"
Dim exp As New RegEx("Key[1-4]: ([" & keys& "])")
For Each item As String in ListBox1.Items
Dim result = exp.Match(item)
If result.Success Then
TextBox1.Text = result.Groups(1).Value
End If
Next
It's not clear to me how your ListBoxes work. If you might find, for example, "Key 2:" inside ListBox1 that you need to ignore, you will want to change the [1-4] part of the expression to be more specific.
Additionally, if you're just trying to exclude unicode or punctuation, you could also go with ranges:
Dim keys As String = "A-Za-z0-9"
If you are supporting a broader set of characters, there are some you must be careful with: ], \, ^, and - can all have special meanings inside of a regular expression character class.
You have multiple keys, I assume you have multiple textboxes to display the results?
Then something like this would work. Loop thru the total number of keys, inside that you loop thru the alphabet. When you find a match, output to the correct textbox:
Dim UsersKey As String
For i As Integer = 1 To 4
For Each c In "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
UsersKey = c
If ListBox1.Items.Contains("Key" & i & ": " & UsersKey) Then
Select Case i
Case 1
TextBox1.Text = UsersKey
Case 2
TextBox2.Text = UsersKey
Case 3
TextBox3.Text = UsersKey
Case 4
TextBox4.Text = UsersKey
End Select
Exit For 'match found so exit inner loop
End If
Next
Next
Also, you say your settings are lost when the filename is changed. I assume when the version changes? The Settings has an upgrade method to read from a previous version. If you add an UpgradeSettings boolean option and set it to True and then do this at the start of your app, it will load the settings from a previous version:
If My.Settings.UpgradeSettings = True Then
My.Settings.Upgrade()
My.Settings.Reload()
My.Settings.UpgradeSettings = False
My.Settings.Save()
End If
Updated Answer:
Instead of using a listtbox, read the settings file line by line and output the results to the correct textbox based on the key...something like this:
Dim settingsFile As String = "C:\settings.txt"
If IO.File.Exists(settingsFile) Then
For Each line As String In IO.File.ReadLines(settingsFile)
Dim params() As String = Split(line, ":")
If params.Length = 2 Then
params(0) = params(0).Trim
params(1) = params(1).Trim
Select Case params(0)
Case "Key1"
Textbox1.Text = params(1)
Case "Key2"
Textbox2.Text = params(1)
End Select
End If
Next line
End If
You can associate text box with a key via its Name or Tag property. Lets say you use Name. In this case TextBox2 is associated with key2. TextBox[N] <-> Key[N]
Using this principle the code will look like this [considering that your list item is string]
Sub Test()
If ListBox1.SelectedIndex = -1 Then Return
Dim data[] As String = DirectCast(ListBox1.SelectedItem, string).Split(new char(){":"})
Dim key As String = data(0).Substring(3)
Dim val As String = data(1).Trim()
' you can use one of the known techniques to get control on which your texbox sits.
' I omit this step and assume "Surface1" being a control on which your text boxes sit
DirectCast(
(From ctrl In Surface1.Controls
Where ctrl.Name = "TextBox" & key
Select ctrl).First()), TextBox).Text = val
End Sub
As you can see, using principle I just explained, you have little parsing and what is important, there is no growing Select case if, lets say, you get 20 text boxes. You can add as many text boxes and as many corresponding list items as you wish, the code need not change.

For Loop: changing the loop condition while it is looping

What I want to do is replace all 'A' in a string with "Bb". but it will only loop with the original string not on the new string.
for example:
AAA
BbAA
BbBbA
and it stops there because the original string only has a length of 3. it reads only up to the 3rd index and not the rest.
Dim txt As String
txt = output_text.Text
Dim a As String = a_equi.Text
Dim index As Integer = txt.Length - 1
Dim output As String = ""
For i = 0 To index
If (txt(i) = TextBox1.Text) Then
output = txt.Remove(i, 1).Insert(i, a)
txt = output
TextBox2.Text += txt + Environment.NewLine
End If
Next
End Sub
I think this leaves us looking for a String.ReplaceFirst function. Since there isn't one, we can just write that function. Then the code that calls it becomes much more readable because it's quickly apparent what it's doing (from the name of the function.)
Public Function ReplaceFirst(searched As String, target As String, replacement As String) As String
'This input validation is just for completeness.
'It's not strictly necessary.
'If the searched string is "null", throw an exception.
If (searched Is Nothing) Then Throw New ArgumentNullException("searched")
'If the target string is "null", throw an exception.
If (target Is Nothing) Then Throw New ArgumentNullException("target")
'If the searched string doesn't contain the target string at all
'then just return it - were done.
Dim foundIndex As Integer = searched.IndexOf(target)
If (foundIndex = -1) Then Return searched
'Build a new string that replaces the target with the replacement.
Return String.Concat(searched.Substring(0, foundIndex), replacement, _
searched.Substring(foundIndex + target.Length, searched.Length - (foundIndex + target.Length)))
End Function
Notice how when you read the code below, you don't even have to spend a moment trying to figure out what it's doing. It's readable. While the input string contains "A", replace the first "A" with "Bb".
Dim input as string = "AAA"
While input.IndexOf("A") > -1
input = input.ReplaceFirst(input,"A","Bb")
'If you need to capture individual values of "input" as it changes
'add them to a list.
End While
You could optimize or completely replace the function. What matters is that your code is readable, someone can tell what it's doing, and the ReplaceFirst function is testable.
Then, let's say you wanted another function that gave you all of the "versions" of your input string as the target string is replaced:
Public Function GetIterativeReplacements(searched As String, target As String, replacement As String) As List(of string)
Dim output As New List(Of String)
While searched.IndexOf(target) > -1
searched = ReplaceFirst(searched, target, replacement)
output.Add(searched)
End While
Return output
End Function
If you call
dim output as List(of string) = GetIterativeReplacments("AAAA","A","Bb")
It's going to return a list of strings containing
BbAAA, BbBbAA, BbBbBbA, BbBbBbBb
It's almost always good to keep methods short. If they start to get too long, just break them into smaller methods with clear names. That way you're not trying to read and follow and test one big, long function. That's difficult whether or not you're a new programmer. The trick isn't being able to create long, complex functions that we understand because we wrote them - it's creating small, simpler functions that anyone can understand.
Check your comments for a better solution, but for future reference you should use a while loop instead of a for loop if your condition will be changing and you're wanting to take that change into account.
I've made a simple example below to help you understand. If you tried the same with a for loop, you'd only get "one" "two" and "three" printed because the for loop doesn't 'see' that vals was changed
Dim vals As New List(Of String)
vals.Add("one")
vals.Add("two")
vals.Add("three")
Dim i As Integer = 0
While i < vals.Count
Console.WriteLine(vals(i))
If vals(i) = "two" Then
vals.Add("four")
vals.Add("five")
End If
i += 1
End While
If you do want to replace one by one instead of using the Replace function, you could use a while loop to look for the index of your search character/string, and then replace/insert at that index.
Sub Main()
Dim a As String = String.Empty
Dim b As String = String.Empty
Dim c As String = String.Empty
Dim d As Int32 = -1
Console.Write("Whole string: ")
a = Console.ReadLine()
Console.Write("Replace: ")
b = Console.ReadLine()
Console.Write("Replace with: ")
c = Console.ReadLine()
d = a.IndexOf(b)
While d > -1
a = a.Remove(d, b.Length)
a = a.Insert(d, c)
d = a.LastIndexOf(b)
End While
Console.WriteLine("Finished string: " & a)
Console.ReadLine()
End Sub
Output would look like this:
Whole string: This is A string for replAcing chArActers.
Replace: A
Replace with: Bb
Finished string: This is Bb string for replBbcing chBbrBbcters.
I was going to write a while loop to answer your question, but realized (with assistance from others) that you could just .replace(x,y)
Output.Text = Input.Text.Replace("A", "Bb")
'Input = N A T O
'Output = N Bb T O
Edit: There is probably a better alternative, but i quickly jotted this loop down, hope it helps.
You've said your new and don't fully understand while loops. So if you don't understand functions either or how to pass arguments to them, I'd suggest looking that up too.
This is your Event, It can be a Button click or Textbox text change.
'Cut & Paste into an Event (Change textboxes to whatever you have input/output)
Dim Input As String = textbox1.Text
Do While Input.Contains("A")
Input = ChangeString(Input, "A", "Bb")
' Do whatever you like with each return of ChangeString() here
Loop
textbox2.Text = Input
This is your Function, with 3 Arguments and a Return Value that can be called in your code
' Cut & Paste into Code somewhere (not inside another sub/Function)
Private Function ChangeString(Input As String, LookFor As Char, ReplaceWith As String)
Dim Output As String = Nothing
Dim cFlag As Boolean = False
For i As Integer = 0 To Input.Length - 1
Dim c As Char = Input(i)
If (c = LookFor) AndAlso (cFlag = False) Then
Output += ReplaceWith
cFlag = True
Else
Output += c
End If
Next
Console.WriteLine("Output: " & Output)
Return Output
End Function

Replacing string at certain index of Split

Using streamreader to read line by line of a text file. When I get to a certain line (i.e., 123|abc|99999||ded||789), I want to replace ONLY the first empty area with text.
So far, I've been toying with
If sLine.Split("|")(3) = "" Then
'This is where I'm stuck, I want to replace that index with mmm
End If
I want the output to look like this: 123|abc|99999|mmm|ded||789
Considering you already have code determining if the "mmm" string needs to be added or not, you could use the following:
Dim index As Integer = sLine.IndexOf("||")
sLine = sLine.Insert(index + 1, "mmm")
You could split the string, modify the array and rejoin it to recreate the string:
Dim sLine = "123|abc|99999||ded||789"
Dim parts = sLine.Split("|")
If parts(3) = "" Then
parts(3) = "mmm"
sLine = String.Join("|", parts)
End If
I gather that if you find one or more empty elements, you want to replace the first empty element with data and leave the rest blank. You can accomplish this by splitting on the pipe to get an array of strings, iterate through the array and replace the first empty element you come across and exit the loop, and then rejoin your array.
Sub Main()
Dim data As String = "123||abc|99999||ded||789"
Dim parts = data.Split("|")
For index = 0 To parts.Length - 1
If String.IsNullOrEmpty(parts(index)) Then
parts(index) = "mmm"
Exit For
End If
Next
data = String.Join("|", parts)
Console.WriteLine(data)
End Sub
Results:
123|mmm|abc|99999||ded||789