How to remove part of a string backwards? - vb.net

I have a vb string with this value: c:\program\bin\files
I need to convert this string to this value: files
All i need is the last folder of the path.
The path is not fixed. It can be anything like: d:\app\win\7\sp1\update
In this case the string must be converted to: update
I think that should be done by searching the string backwards for the first occurrence of \ and removing everything before it, including the \ itself.
But obviously i don't know how to do it. :)
Thank you!
EDIT:
I need to use this to fill a ComboBox...
This is what i am using:
ComboBox1.Items.AddRange(IO.Directory.GetDirectories(appPath & "\Updates\Version"))
It gives me a ComboBox like:
c:/program/Updates/Version/Beta1
c:/program/Updates/Version/Beta2
c:/program/Updates/Version/Beta3
c:/program/Updates/Version/Beta4
And i would like to have the ComboBox like:
Beta1
Beta2
Beta3
Beta4

Rather than try to pull apart the string yourself, have a look at the System.IO.Path class.
In your case, the GetFileName method does what you want:
lastFolder = System.IO.Path.GetFileName(path)
To fill a combo box with names you can use a LINQ query like this:
ComboBox1.Items.AddRange(
From path
In IO.Directory.GetDirectories(IO.Path.Combine(appPath, "Updates\Version"))
Select IO.Path.GetFileName(path))
Also, try to use the Path.Combine method when joining path fragments together. It's safer than just joining strings.

In VBScript, which this is tagged you have two choices. This was written before any code was edited into the question.
Use InstrR which is Instr but from back to front of string.
You also have StrReverse which reverses a string.
InStrRev
Returns the position of an occurrence of one string within another, from the end of string.
InStrRev(string1, string2[, start[, compare]])
StrReverse
Returns a string in which the character order of a specified string is reversed.
StrReverse(string1)
If using the File System Object it has a method to do specifically what you want.
GetParentFolderName Method
Returns a string containing the name of the parent folder of the last component in a specified path.
Set fso = CreateObject("Scripting.FileSystemObject")
object.GetParentFolderName(path)

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

Limiting the amount of files grabbed from system.io.directory.getfiles

I've got a folder browser dialogue populating the directory location (path) of a system.io.directory.getfiles. The issue is if you accidentally select a folder with hundereds or thousands of files (which there's no reason you would ever need this for this app) it will lock up the app while it grabs all the files. All I'm grabbing are the directory locations as strings and want to put a limit on the amount of files that can be grabbed. Here's my current code that isn't working.
If JigFolderBrowse.ShowDialog = DialogResult.OK Then
Dim dirs(50) As String
dirs = System.IO.Directory.GetFiles(JigFolderBrowse.SelectedPath.ToString, "*", System.IO.SearchOption.AllDirectories)
If dirs.Length> 50 Then
MsgBox("Too Many Files Selected" + vbNewLine + "Select A Smaller Folder To Be Organized")
Exit Sub
End If
'Seperate Each File By Type
For i = 0 To dirs.Length - 1
If Not dirs(i).Contains("~$") Then
If dirs(i).Contains(".SLDPRT") Or dirs(i).Contains(".sldprt") Then
PartsListBx.Items.Add(dirs(i))
ElseIf dirs(i).Contains(".SLDASM") Or dirs(i).Contains(".sldasm") Then
AssemListBx.Items.Add(dirs(i))
ElseIf dirs(i).Contains(".SLDDRW") Or dirs(i).Contains(".slddrw") Then
DrawingListBx.Items.Add(dirs(i))
ElseIf dirs(i).Contains(".pdf") Or dirs(i).Contains(".PDF") Then
PDFsListBx.Items.Add(dirs(i))
ElseIf dirs(i).Contains(".DXF") Or dirs(i).Contains(".dxf") Then
DXFsListBx.Items.Add(dirs(i))
ElseIf Not dirs(i).Contains(".db") Then
OtherFilesListBx.Items.Add(dirs(i))
End If
End If
The Directory.GetFiles method always retrieves the full list of matching files before returning. There is no way to limit it (outside of specifying a more narrow search pattern, that is). There is, however, the Directory.EnumerateFiles method which does what you need. From the MSDN article:
The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array. Therefore, when you are working with many files and directories, EnumerateFiles can be more efficient.
So, for instance, you could do something like this:
dirs = Directory.
EnumerateFiles(
JigFolderBrowse.SelectedPath.ToString(),
"*",
SearchOption.AllDirectories).
Take(50).
ToArray()
Take is a LINQ extension method which returns only the first x-number of items from any IEnumerable(Of T) list. So, in order for that line to work, you'll need to import the System.Linq namespace. If you can't, or don't want to, use LINQ, you can just implement your own method that does the same sort of thing (iterates an IEnumerable list in a for loop and returns after reading only the first 50 items).
Side Note 1: Unused Array
Also, it's worth mentioning, in your code, you initialize your dirs variable to point to a 50-element string array. You then, in the very next line, set it to point to a whole new array (the one returned by the Directory.GetFiles method). While it's not breaking functionality, it is unnecessarily inefficient. You're creating that extra array, just giving the garbage collector extra work to do, for no reason. You never use that first array. It just gets dereferenced and discarded in the very next line. It would be better to create the array variable as null:
Dim dirs() As String
Or
Dim dirs() As String = Nothing
Or, better yet:
Dim dirs() As String = Directory.
EnumerateFiles(
JigFolderBrowse.SelectedPath.ToString(),
"*",
SearchOption.AllDirectories).
Take(50).
ToArray()
Side Note 2: File Extension Comparisons
Also, it looks like you are trying to compare the file extensions in a case-insensitive way. There are two problems with the way you are doing it. First, you only comparing it against two values: all lowercase (e.g. ".pdf") and all uppercase (e.g. ".PDF). That won't work with mixed-case (e.g. ".Pdf").
It is admittedly annoying that the String.Contains method does not have a case-sensitivity option. So, while it's a little hokey, the best option would be to make use of the String.IndexOf method, which does have a case-insensitive option:
If dirs(i).IndexOf(".pdf", StringComparison.CurrentCultureIgnoreCase) <> -1 Then
However, the second problem, which invalidates my last point of advice, is that you are checking to see if the string contains the particular file extension rather than checking to see if it ends with it. So, for instance, a file name like "My.pdf.zip" will still match, even though it's extension is ".zip" rather than ".pdf". Perhaps this was your intent, but, if not, I would recommend using the Path.GetExtension method to get the actual extension of the file name and then compare that. For instance:
Dim ext As String = Path.GetExtension(dirs(i))
If ext.Equals("pdf", StringComparison.CurrentCultureIgnoreCase) Then
' ...

Why isn't my String updating?

I have a small string variable with a length of 400-500 characters spaces included. I've tried a few things as far as removing some parts of the string, while in a Do/While loop. I'm looking to go through the loop, then remove 300 characters from it, however it doesn't seem to be actually removing it from the string.
Are we not actually able to modify a string object and must be forced to SubString the text variable to get the desired result?
Do while stringText.Length >= 300
'stringText.replace(textToRemove, "") This doesn't replace the string variable
'stringText.Remove(0,299) This also doesn't remove the specified range of characters
Loop
Strings in .NET and VB are immutable, meaning that a string can never change once it's been defined. What the various Replace/Remove methods do is return a new, modified string, which you can store to the same variable.
Like this:
Do while stringText.Length >= 300
stringText = stringText.Replace(textToRemove, "")
Loop
It's important to note, though, that this is potentially expensive - a new string object is allocated. If you have a lot of modifications to make to a string separately, each one will create a new copy, and for large strings it might create unnecessary memory allocations.
For this reason, we have the System.Text.StringBuilder class (as mentioned by roryap), which lets us manipulate strings directly. Read up on it.
These functions return a value, you must assign the value returned to the original string for it to be updated:
e.g.
stringText = stringText.replace(textToRemove, "")
Otherwise you are just discarding the value returned - the functions do not mutate the original string
You have to assign string for the return value
stringText =stringText.replace(textToRemove, "")

CSV Data handling - vb.net

I've been asked at work to create a project to open a CSV and then use a set of conditions to change and save the data using Visual Basic.net (2010)
Although I am comfortable creating files in vb and opening them again into vb, I don't know how to declare the fields so I can query them. For example:
if field1 = "Yes" and field2 = "Blue" then textbox1.text = "abcd"
Then at a later stage I want to export a file which I'm happy doing where it writes lines to create a new CSV which could be Field1, textbox1.text, Field3 and so on
Also, would I have to declare line1.field1 and line2.field1 or could I declare line2.field1 as field25 for example or whatever the next sequential number may be?
Thanks
How are you going to read from the file?
If you do File.ReadAllLines
what does it return? A string array.
Does a string array have properties like line1? No, but they do have indexers.
How do you access an element in a string array? With a indexer,
e.g. array(0).
Does an string have properties like field1? Nope.
Can you use String.Split to split on the commas and separate the fields? Yes.
Could you write a class that has specific properties defined for each field that has a constructor that'd take a string that represents a row and put the value into the correct fields? Yes.
Could the same class know how to convert itself into a single CSV style line? Yup.
Are there other library that could help you do this? Probably.
All that being said, you can probably get away with doing something simple like this (warning: naive code sample):
Dim fileName = "C:\testFile.csv"
Dim lines = File.ReadAllLines(fileName)
Dim output As New List(Of String)
For Each line In lines
Dim fields = line.Split(","c)
fields(0) = "000" 'Blank out number
If fields(3) = "Y" Then 'Change Y to True
fields(3) = "True"
End If
output.Add(String.Join(","c, fields))
Next
File.WriteAllLines(fileName, output)
I gave it input that looked like this:
123,abc,Y,Y,N
456,def,Y,N,Y
789,ghi,N,Y,Y
012,jkl,N,N,N
and it changed the file to this:
000,abc,Y,True,N
000,def,Y,N,Y
000,ghi,N,True,Y
000,jkl,N,N,N
Utilities for working with CSV will do a better job than this. There are various ways this won't work (doesn't handle any escape sequences, etc.) but this could be sufficient if you're just wanting to do something quick and dirty and don't need to worry about some things. At the very least hopefully it'll give you a better understanding of how you'd go about solving a problem like this.
I'd recommend writing the output to a different file to test.

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.