So I have a a little issue....
I have a string that looks like this...
O''Neil
is there a way for me to remove one of the apostrophes? Basically I'd like to convert it back to
O'Neil
Hopefully this can help you:
Note - I left the Error handling to you.
Imports System
Public Module Module1
Public Sub Main()
Dim data As String = "O''Neil"
Dim in1 As Integer = data.IndexOf("'"C) ' get the index of first quote
Dim in2 As Integer = data.IndexOf("'"C, in1 + 1) ' get the index of second
Dim result As String = data.Remove(in2, 1) ' remove the second quote
Console.WriteLine(result)
End Sub
End Module
Related
I read a text file, remove all punctuations and than read all the words in a String(). I want to count the words so I need some String() with two fields, word and frequency. Before I add a word I count the amount of times it is occuring in the text with the Function CountMyWords. If the word already is in the String() I dont want to add it again, just increase it's frequency.
Private Sub CreateWordList()
Dim text As String = File.ReadAllText("C:\Users\Gebruiker\Downloads\shakespear.txt")
text = Regex.Replace(text, "[^A-Za-z']+", " ")
Dim words As String() = text.Split(New Char() {" "c})
Dim i As Integer
For Each word As String In words
If Len(word) > 5 Then
word = word.ToLower()
'now check if the word already exists
If words.Contains(word) = True Then
End If
i = CountMyWords(text, word)
Console.WriteLine("{0}", word + " " + i.ToString)
End If
Next
End Sub
Private Function CountMyWords(input As String, phrase As String) As Integer
Dim Occurrences As Integer = 0
Dim intCursor As Integer = 0
Do Until intCursor >= input.Length
Dim strCheckThisString As String = Mid(LCase(input), intCursor + 1, (Len(input) - intCursor))
Dim intPlaceOfPhrase As Integer = InStr(strCheckThisString, phrase)
If intPlaceOfPhrase > 0 Then
Occurrences += 1
intCursor += (intPlaceOfPhrase + Len(phrase) - 1)
Else
intCursor = input.Length
End If
Loop
CountMyWords = Occurrences
End Function
Any thought how to do that?
As with Steve's answer, I suggest using a Dictionary, but you might not need the overhead of having a class as the value in the dictionary.
Also, if you're using fairly large files, you can process them one line at a time with the File.ReadLines method instead of reading the whole lot into RAM.
You can make the processing of the text a little terser with some LINQ, like this:
Imports System.IO
Imports System.Text.RegularExpressions
Module Module1
Sub Main()
' using https://raw.githubusercontent.com/brunoklein99/deep-learning-notes/master/shakespeare.txt
Dim src = "C:\temp\TheSonnets.txt"
Dim wordsWithCounts As New Dictionary(Of String, Integer)
For Each line In File.ReadLines(src)
Dim text = Regex.Replace(line, "[^A-Za-z']+", " ")
Dim words = text.Split({" "c}).
Where(Function(s) s.Length > 5).
Select(Function(t) t.ToLower())
For Each w In words
If wordsWithCounts.ContainsKey(w) Then
wordsWithCounts(w) += 1
Else
wordsWithCounts.Add(w, 1)
End If
Next
Next
' extracting some data as an example...
Dim mostUsedFirst = wordsWithCounts.
Where(Function(x) x.Value > 18).
OrderByDescending(Function(y) y.Value)
For Each w As KeyValuePair(Of String, Integer) In mostUsedFirst
Console.WriteLine(w.Key & " " & w.Value)
Next
Console.ReadLine()
End Sub
End Module
With the example text, this outputs:
beauty 52
should 44
though 33
praise 28
love's 26
nothing 19
better 19
I would use a different approach. The first thing to do is to create a class that represent the word frequency. It is just a string for the word and an integer to count the word repetitions
Public Class WordFrequency
Public Property Word As String
Public Property Frequency As Integer
End Class
Now, you can create a dictionary where the key is the word and the value is an instance of the WordFrequency class. Using a dictionary is a great bonus in searching if an item exists in the collection. You use a syntax similar to the one used for arrays and specific methods exist to find the element in the collection. So your code becomes simply
' Declared at the global class level
Dim wordCounter As Dictionary(Of String, WordFrequency) = New Dictionary(Of String, WordFrequency)
.....
Private Sub CreateWordList()
Dim text As String = File.ReadAllText("C:\Users\Gebruiker\Downloads\shakespear.txt")
text = Regex.Replace(text, "[^A-Za-z']+", " ")
' remove any blank entries eventually created by the replace
Dim words As String() = text.Split(New Char() {" "c}, StringSplitOptions.RemoveEmptyEntries)
For Each word As String In words
If word.Length > 5 Then
word = word.ToLower()
' If we don't have the word in the dictionary, create the entry
If Not wordCounter.ContainsKey(word) Then
wordCounter.Add(word, New WordFrequency With
{
.Word = word,
.Frequency = 0
})
End If
' just increment the property frequency from the dictionary Value
wordCounter(Word).Frequency += 1
End If
Next
End Sub
Note that instead of having a Value of type WordFrequency you can just use an integer for the frequency, but I prefer to have a class because if you ever need to expand the informations kept in the dictionary a class will be easily extended
I suggest a Dictionary:
Public Function CountWords(words As IEnumerable(Of String)) As Dictionary(Of String, Integer)
Dim result As New Dictionary(Of String, Integer)()
For Each word As String In words
If result.ContainsKey(word)
result(word)+=1
Else
result.Add(word, 1)
End If
Next
Return result
End Function
Private Sub CreateWordList(filePath As String)
Dim text As String = File.ReadAllText(filePath).ToLower()
text = Regex.Replace(text, "[^a-z']+", " ")
Dim words As IEnumerable(Of String) = text.Split(New Char() {" "c}).
Where(Function(w) w.Length > 5)
Dim wordCounts As Dictionary(Of String, Integer) = CountMyWords(words)
For Each kvp As KeyValuePair(Of String, Integer) In wordCounts
Console.WriteLine($"{kvp.Key} {kvp.Value}")
Next
End Sub
I'm trying to delete the ending part of a string where the beginning and end are variable but have several indicators. The string I'm working with is "Num1_xc_min_20201229_112401.rdf".
The Num1 is variable and the 20201229_112401 is variable (since it's a date). Num1 will not always have four characters. The end result I want is "Num1". xc is always constant.
Here is the code I'm working with:
Sub Macro1()
Dim input1 As String
Dim remove1 As String
Dim result1 As String
input1 = Range("C2").Value
remove1 = "_xc_*" 'this is the char to be removed
result1 = Replace(input1, LCase(remove1), "")
Range("C2").Value = result1
End Sub
This hasn't worked because you can't set a variable equal to a like statement.
Split by the remove1 value and return the first (0-th) element:
Sub Macro1()
Dim input1 As String
Dim remove1 As String
input1 = "Num1_xc_min_20201229_112401.rdf"
remove1 = "_xc_"
Debug.Print Split(input1, remove1)(0)
End Sub
Returns Num1.
From ActiveWorkbook.name, I would like to extract the strings that are before (left side of ) the numbers. Since I want to use the same code in multiple workbooks, the file names would be variable, but every file name has date info in the middle (yyyymmdd).
In case of excel file, I can use the below formula, but can I apply the same kind of method in VBA?
=LEFT(A1,MIN(FIND({0,1,2,3,4,5,6,7,8,9},ASC(A1)&1234567890))-1)
Example: MyExcelWorkbook_Management_20200602_MyName.xlsm
In above case, I want to extract "MyExcelWorkbook_Management_".
The most basic thing you could do is to replicate something that worked for you in Excel through Evaluate:
Sub Test()
Dim str As String: str = "MyExcelWorkbook_Management_20200602_MyName.xlsm"
Debug.Print Evaluate(Replace("=LEFT(""X"",MIN(FIND({0,1,2,3,4,5,6,7,8,9},ASC(""X"")&1234567890))-1)", "X", str))
End Sub
Pretty? Not really, but it does the job and got it's limitations.
You could use Regular Expressions to extract any letters / underscores before the number as well
Dim str As String
str = "MyExcelWorkbook_Management_20200602_MyName.xlsm"
With CreateObject("vbscript.regexp")
.Pattern = "^\D*"
.Global = True
MsgBox .Execute(str)(0)
End With
Gives:
MyExcelWorkbook_Management_
So basically you want to use the Midfunction to look for the first numerical character in your input string, and then cut your input string to that position.
That means we need to loop through the string from left to right, look at one character at a time and see if it is a digit or not.
This code does exactly that:
Option Explicit
Sub extratLeftText()
Dim someString As String
Dim result As String
someString = "Hello World1234"
Dim i As Long
Dim c As String 'one character of your string
For i = 1 To Len(someString)
c = Mid(someString, i, 1)
If IsNumeric(c) = True Then 'should write "If IsNumeric(c) = True AND i>1 Then" to avoid an "out of bounds" error
result = Left(someString, i - 1)
Exit For
End If
Next i
MsgBox result
End Sub
Last thing you need to do is to load in some workbook name into your VBA function. Generally this is done with the .Name method of the workbookobject:
Sub workbookName()
Dim wb As Workbook
Set wb = ActiveWorkbook
MsgBox wb.Name
End Sub
Of course you would need to find some way to replace the Set wb = ActiveWorkbook line with code that suits your purpose.
I was following some examples online in which im trying to pass a value from one sub into a another sub in VBA but get the error:
Compile Error:
Procedure declaration does not match description of event or procedure having the same name.
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Call set_button_Click(nextsat)
End Sub
Sub set_button_Click(ByRef nextsat As String)
......
End Sub
Change the sub name in something else like SetButtonOnClick.
The _Click keyword is reserved by excel for the Click event on buttons if you have a button called with the same name.
You can't change the parameters for an event handler (except for the parameter name). That also means you can't add any parameters if none are expected. Not even Optional ByRef nextsat As String will work.
There are three ways to pass a value between event handlers in a UserForm:
Using a global variable (not recommended, ever);
Via the UserForm.tag property (recommended for simple values such as strings). Obviously cannot be used if it already has a permanent use;
Via one or more hidden controls (recommended for multiple or complex values as well as simple ones).
I've used the second method:
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = Weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Me.Tag = nextsat
End Sub
Sub set_button_Click()
Dim nextsat As String
nextsat = Me.Tag
......
End Sub
A better solution in your case might be to have a visible TextBox in which you store the calculated date when the user clicks next_sat, so that the user can see it. Then in your set_button handler, grab it from TextBox.Text.
Im trying to display an empty string which is pretty straight forward, how is their a way to display an empty number for an integer? i have the example below.
Sub()
Dim s As String
Dim Number As Integer
'using a space in double quote
s = " "
'this will display an empty string, well not really empty but the space will make it empty
msgbox(s)
so basically im trying to do the same thing integer whereas when you use a msgbox for it
it displays nothing.
You should make a function for your needs
function ShowNumberNonZero( n ) as string
if n = 0 then
ShowNumberNonZero = ""
else
ShowNumberNonZero = Trim(n)
end if
end function
and then
msgbox(ShowNumberNonZero(Number))
If you don't set your integer at all, calling MsgBox will assign it 0 and it will display 0. The only way to make it so that it will display nothing would be to use an if statement or a function.
It might look something like this:
Private Sub Run()
Dim number As Integer
Message(number)
End Sub
Private Sub Message(ByVal input As Integer)
If input = 0 Then
MsgBox("")
Else
MsgBox(input)
End If
End Sub