A Perfectly good Substring Error - vb.net

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.

Related

How can I split names of directories in certain path from its full path (VB.NET)

Hello world!
I've ran into a problem. I am getting directories contained in certain path and I need to separate the path VB.NET's giving me (like this:
"D:\ApplicationFolder\Addons\Pack_1",
"D:\ApplicationFolder\Addons\Pack_2" ...
Only into this:
"Pack_1", "Pack_2"
So far I've tried this, but I can't get into a solution, I am lost...
Dim ADDONPACKS_DIRECTORIES As String() = Directory.GetDirectories(ADDONS_PATH) ' GETTING ALL DIRECTORIES (PATHS) IN THIS PATH
For Each ADDONPACKS_DIRECTORY In ADDONPACKS_DIRECTORIES ' TRYING TO SPLIT FULL PATH OF THESE DIRECTORIES TO GET ONLY THE NAME OF THESE DIRECTORIES
ADDONPACKS_DIRECTORY.Split()
Dim ADDONPACKS_LENGTH As Integer = ADDONPACKS_DIRECTORY.Length()
MsgBox(ADDONPACKS_DIRECTORY(2))
Next
' Here I want to assign names of these directories onto a label. But the fields only show letters instead of the path segments.
Addonpack1.Text = ADDONPACKS_DIRECTORIES(0)
Addonpack2.Text = ADDONPACKS_DIRECTORIES(1)
Addonpack3.Text = ADDONPACKS_DIRECTORIES(2)
Addonpack4.Text = ADDONPACKS_DIRECTORIES(3)
Addonpack5.Text = ADDONPACKS_DIRECTORIES(4)
'Addonpack6.Text = ADDONPACKS_DIRECTORY(5)
Any ideas? I really appreciate further help.
string.Split() is a Function: it returns a value.
Here: ADDONPACKS_DIRECTORY.Split(), you are splitting the string using the default separator (a white space) but the result is not assigned to anything, so it's lost (but it wouldn't be useful anyway).
This: MsgBox(ADDONPACKS_DIRECTORY(2)), will show only one char of the current Directory path. A string is a collection (an array) of chars. You're asking to show the 3rd.
If you think you won't need the complete directory listing anymore, you could Split the initial collection directly:
Dim ADDONPACKS_DIRECTORIES As String() = Directory.GetDirectories(ADDONS_PATH).
Select(Function(d) d.Split("\"c).Last()).ToArray()
Addonpack1.Text = ADDONPACKS_DIRECTORIES(0)
'(...)
If you instead are going to use that collection of Paths later, you could Split each path and assign the result to each TextBox.Text property, leaving the original collection untouched:
Addonpack1.Text = ADDONPACKS_DIRECTORIES(0).Split("\"c).Last()
Addonpack2.Text = ADDONPACKS_DIRECTORIES(1).Split("\"c).Last()
'(...)
Do you know beforehand how many Addons you will have?
If not, a TextBox for each path might not be the right object to use as the output.
Maybe, you could use a single multiline TextBox. It's Lines() property will hold the array of all the Sub-Paths you appended.
Using the first snippet, it could be something like this:
For Each subpath As String In ADDONPACKS_DIRECTORIES
TextBox1.AppendText(subpath & Environment.NewLine)
Next
Note:
As LarsTech noted in the comments, you could use Path.GetFileName() insted of splitting the path using the path separator.
It would work with both file names and path names, because Path.GetFileName returns the substring of a path when it first finds a path separator, parsing the string from the end to the start, no matter if the substring represents a path or a file name.
Addonpack1.Text = Path.GetFileName(ADDONPACKS_DIRECTORIES(0))
'(...)

Parsing string - "Contains" is insufficient

I use this code to check if a String is in another String:
If StringData(1).Contains("-SomeText2.") Then
'some code
End If
'StringData(1) looks like this:
'-SomeText1.1401-|-SomeText2.0802-|-SomeText3.23-|-SomeText4.104-|
'In case I look for -SomeText1. I need 1401
'In case I look for -SomeText2. I need 0802
'In case I look for -SomeText3. I need 23
'In case I look for -SomeText4. I need 104
I first check if -SomeText2. is in StringData(1), and if it is, I need to get the next part of the text: 0802 which is the part I don't know how to do, how can I do it?
All the strings are separated by | and all substrings start and end with - and have a . separating the first part from the second. I check all the strings starting with - and ending with . because there are some with - and | in the middle, so Split function won't work.
Those strings change quite often, so I need something to check it no matter the length of the strings.
I would just split the string up and get the text between "." and "-" when the search text is found like this:
Dim str As String = "-SomeText1.1401-|-SomeText2.0802-|-SomeText3.23-|-SomeText4.104-"
Dim searches() As String = {"-SomeText1", "-SomeText2", "-SomeText3", "-SomeText4"}
For Each search As String In searches
For Each value As String In str.Split(CChar("|"))
If value.Contains(search) Then
Dim partIwant As String = value.Substring(value.IndexOf(".") + 1, value.Length - value.IndexOf(".") - 2)
MsgBox(partIwant)
'Outputs: 1401, 0802, 23, 104
Exit For
End If
Next
Next
In this example, we just use Contains() to see if our search string is present or not...we can't actually use that function to get any further information because all it returns is a True or False. So once we know that our string has been found, it's just a matter of some string manipulation to grab the text between the "." and "-" characters. IndexOf() will get us the index of the period, and then we just pull the text between there and the last character of the string.
Your question has nothing to do with WPF, so the tag and title are misleading.
To solve your problem, you should use String.IndexOf(string) instead of String.Contains(string). That tells you at which position the given string starts. If that value is -1, it means that the original string does not contain your search string at all.
Once you have that starting index, you can use String.IndexOf(string, int) to search for the next occurrence of -, so you know where the entry stops. The second parameter tells it at which index it should start the search, and in this case you should start the search at the index where you found your first match.
Now that you know the starting index of your match, the end index of the entry and the length of your search string, you can put those together and easily use String.Substring(int, int) to get the part of the string that you are interested in.
That's the straight forward, naive solution. A more sophisticated solution would simply build a regular expression for the search string that is built in a way that the part you are interested in is included in the capture group. But that's a more elaborate topic.

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

VBA error: A value used in the formula is the wrong data type?

I'm trying to create a function which generates a secret encoded message. It takes in three things: a string, for example, "testingtestingonetwothree", as well as the desired number of characters, for example 5, and the desired number of words, for example 5. It generates the message by starting at the first character, and extracting every fifth character through the string, putting these characters into a codeword, then starting at the second character and extracting every fifth character through the string, putting these into a second codeword, and so on. It just outputs a string, with the codewords separated by a space. So for this example it would produce: "tntnt egieh stntr tegwe isooe".
I'm okay at coding but new to VBA. I've made what I think is a valid function, but when it's used in the spreadsheet I get a #VALUE! error: "A value used in the formula is the wrong data type". This is the user defined function I made:
Function encode(strng, numchars, numwords)
Dim word As Integer
Dim step As Integer
Dim temp As String
Dim output As String
For word = 1 To numchars
step = word
temp = ""
Do While step <= Len(strng)
temp = temp & Mid(strng, 1, step)
step = step + numchars
Loop
If word = 1 Then output = temp Else output = output & " " & temp
Next word
encode = output
End Function
And when it's used in the spreadsheet I just call it, as in
=encode(A16,A7,A10)
Where A16 contains testingtestingonetwothree, A7 contains 5 and A10 contains 5.
Does my function seem okay? And is there anything you guys can see which could be giving the value error? Any help would be greatly appreciated, thanks a lot for reading.
EDIT: This now outputs a value, but the wrong value. It outputs: "ttestintestingtesttestingtestingontestingtestingonetwot tetestingtestingtestitestingtestingonetestingtestingonetwoth ", when it should output: "tntnt egieh stntr tegwe isooe". Is there anything you guys can see that my function is doing wrong?
EDIT2: After fixing the Mid function, to
temp = temp & Mid(strng, step, 1)
as per vacip's answer, the function now produces the correct answer.
Ok, everyone says it works, but for me, it doesn't produce the desired output. What the...???
Anyway, I think your Mid function is in the wrong order, try it like this:
temp = temp & Mid(strng, step, 1)
Also, make sure to properly declare your variables, like this:
Function encode(strng As String, numchars As Integer, numwords As Integer) As String
I have also rewritten your IF statement, that one-line thing is strange for me...
If word = 1 Then
output = temp
Else
output = output & " " & temp
End If
This way it worked for me.
Other people have addressed the type problem. Here is a different suggestion. The cipher that you are describing is a simple transposition cipher, specifically a columnar transposition ( https://en.wikipedia.org/wiki/Transposition_cipher#Columnar_transposition )
The way people did this pre-computer was to write the characters into a grid row by row then read them off column by column. In fact -- this is probably still the easiest way to implement it even with computers. Declare a variant which can be redimensioned to be an array with e.g. 5 columns (where 5 is the skip between letters) and the number of rows is chosen to be large enough so that the grid can hold the string. After you load up the characters row by row, read them off column by column using nested for loops.
Once you get a basic example working, you can try to implement a version which uses a key to determine the order that you read off the columns for added security.
Coding classical cryptography/cryptanalysis as an excellent way to learn a programming language. Almost the first thing I do when I try to learn a new language is to implement a Vigenere cipher in it. Even though it is long out of print and can be somewhat tricky to translate to modern dialects of Basic the book "Cryptanalysis for Microcomputers" by Caxton Foster is great fun and can be purchased for just a few dollars from online used bookstores.
You need to define your Function's type. So in this case I believe you would want
Function encode(strng, numchars, numwords) As String
I tested your code exactly as it is, and it worked fine.
So, your problem may be:
A certain argument of your function is not the right type. (I bet the len method is the problem in there).
Check if A16 is really a string. If not, consider converting it to a string before if you want to pass numbers too:
Function encode(strng as variant, numchars as integer, numwords as integer) as string
strng = str(strng)
Check also if A7 and A10 are really integers.

MS-Access: Replace "bracket"

In one of the ms-access table I work with we have a text field with a set size.
At the end of this field there is some extra code that varies depending on the situation.
I'm looking for a way to remove one of these code but even when the last part is truncated by the field maximum size.
Let's call the field "field" and the code I'm looking to remove "abc-longcode".
If I use the replace SQL function with the string abc-longcode the query will only work when the code is complete.
If I also want my update query (that does nothing but remove this specific code at the end of my field) to work on incomplete codes how would that translate into ms-SQL?
It would have to remove (or replace with "" to be precise) all of the following (example of course, not the real codes):
abc-longcode
abc-longcod
abc-longco
abc-longc
abc-long
abc-lon
abc-lo
abc-l
Obviously I could do that with several queries. Each one replacing one of the expected truncated codes... but it doesn't sound optimal.
Also, when the field is big enough to get all of the code, there can sometime be extra details at the end that I'll also want to keep so I cannot either just look for "abc-l" and delete everything that follows :\
This query (or queries if I can't find a better way) will be held directly into the .mdb database.
So while I can think of several ways to do this outside of a ms-sql query, it doesn't help me.
Any help?
Thanks.
You can write a custom VBA replace method that will replace any of the given cases {"abc-longcode", ... "abc-l"}. This is essentially the same tack as your "several queries" idea, except it would only be one query. My VBA is rusty, but something like:
public function ReplaceCodes(str as string) as string
dim returnString as string
returnString = str
returnString = replace(returnString,"abc-longcode","")
// ... etc...
ReplaceCodes = returnString
end function
I may have gotten the parameter order wrong on replace :)
I would use my own custom function to do this using the split function to get the first part of the string. You can then use that value in the update query.
Public Function FirstPart(thetext As String) As String
Dim ret As String
Dim arrSplitText As Variant
arrSplitText = Split(thetext, "-")
ret = arrSplitText(0)
FirstPart = ret
End Function
Can you use:
Left(FieldX,InStr(FieldX,"abc-")-1)
EDIT re Comment
If there is a space or other standard delimiter:
IIf(InStr(InStr(FieldX, "abc-"), FieldX, " ") = 0, Left(FieldX, InStr(FieldX, "abc-") - 1), Replace(FieldX, Mid(FieldX, InStr(FieldX, "abc-"), InStr(InStr(FieldX, "abc-"), FieldX, " ") - InStr(FieldX, "abc-")), ""))