How to recognize a path inside a string - c#

Just that... I get a string which contains a path to a file plus some arguments. How can I recognize the path? I thought about the index of the '.' in the file... but I don't like it.
What about using regular expressions? Can anyone point me in the right direction?
Regards
Edit: Theses are valid entries...
somefile.msi /a
C:\MyFolder\SomeFile.exe -i -d
I don't care much about the arguments cause once I have the path I'll assume the rest are arguments

You can use System.IO.Path, and it's static methods.
bool isPath = System.IO.Path.GetDirectoryName(#"C:\MyFolder\SomeFile.exe -i -d") != String.Empty;
if (isPath)
{
Console.WriteLine("The string contains a path");
}
The static Path class has several other methods which are useful as well, like .GetFilename, .GetExtension and .GetPathRoot.
You can also probably use System.IO.Directory and System.IO.File for additional features.

You can't unless you access the file system, because paths may contain spaces.
So you might test each possible "file" using File.Exists. A string.Split() will help you here.

For non-MSI programs, UninstallString is passed to CreateProcess, so you probably want to replicate its method of determining the filename. Read http://msdn.microsoft.com/en-us/library/ms682425.aspx, especially the notes for lpApplicationName and the second half of lpCommandLine.
Programs installed by MSI have a separate system (msi!MsiConfigureProduct or msi!MsiRemovePatches).

Well, before you tackle finding the path in the string, you need to outline a set of rules for what a path in this context
This is, for the operating system, a valid filename: a
Since no directory information is specified, the current directory will be used. The file has no extension.
But is still a filename.
Is this a path in your context? Or do you mean something that has directory information as well?
Examples of what you need to handle would be useful.

The normal way in this case is to tokenise by spaces (and use quotes for a filename with spaces in). Then use / or - for arguments. I think you'll be better off using a standard, accepted format than working for only a subset of cases.

Mmm, I believe that the only reliable way to do that is to access the filesystem, supposing it is reachable.
I would cut the string at the spaces, starting from the end, and take the longest that exist on the filesystem.
For example:
C:\My Folder\Some File.exe -i -d
=>
C:\My Folder\Some File.exe -i -d (no, although it might exist!)
C:\My Folder\Some File.exe -i (no)
C:\My Folder\Some File.exe (yes => That's this one)
You must take in account relative paths, and files in PATH (like your first example, ie. all exe files - even worse, you can write foo.exe or foo on the command line!).
Plus you can often write stuff like notepad/p, which doesn't simplify the algorithm, knowing that C:/windows/notepad.exe is a valid path in XP! :-)

If you have any control over the string I'd recommend you change the way it is represented. One way would be to use URL-style parameters - e.g. fileName=myFile;arg1=value, etc. Then it's trivial to parse on keys.
The problem with any sort of raw parsing scheme is that the trailing data is not necessarily going to be clean. Also your filename is not necessarily NOT going to have spaces, extraneous "."'s etc. So whatever you decide is liable to not be correct 100% of the time.

Have a look at SYstem.IO.Path, you probably want GetDirectoryName(string)

thanks all for the answers.. there are a couple of posts I'd like to mark as the answer... what do you do in these cases?
Just in case somebody wants to see what I'm working on, it's a small application used to uninstall software from my computer. I posted the code, so feel free to download it and take a look at it.
The reason to this post is a good way to implement the private ProcessStartInfo GetProcessInfo(string uninstallString) method.
The reason to this project is posted here...

Related

Can't get to files with multiple consecutive spaces at the end of a FOLDER name

I'm trying to programmaticaly locate a file on a network server where there are multiple directory levels, one of which has two spaces at the end of the name. EG:
\\MyServer\C$\TopLevel\Account \someone#somewhere.com\AFileName.pdf
Note that the Account level represents many folders using account codes for the folder name, and most of them have the two spaces at the end.
I can't amend the directory structure as it's used by other processes outside my control. I will know in advance the path down to and including the Account level, so I need to search the Email level subfolders for a file containing an order number in the file name. I also know the order number.
The problem is that FileInfo, Directory and DirectoryInfo all balk at the two spaces. Whilst Windows has allowed the folders to be created, and an automated third party process can create folders and files below the Account level, nothing I have tried so far has worked because the spaces get stripped out by the c# file and directory classes.
I can't use:
Directory.GetFiles(PathToTopLevel, "*.pdf")
because there are over 1000 Account level folders, each of which might have 5 or 6 Email level folders with possibly many hundreds of pdf files in each one. I did try it but had to kill it when it was still thinking about it after 5 minutes.
Directory.GetFiles(PathToAccountLevel, "*.pdf")
This returns the 'can't find part of the path' error.
I also tried:
filePath = Path.Combine(PathToAccountLevel, $"{order_no}.pdf");
var fi = new FileInfo(filePath);
which also returns 'can't find part of the path'.
I've done lots of trawling through SO and other forums with no success as they are mainly concerned either with file names or single spaces, but I did find a useful method here which I have tried:
filePath = AddQuotesIfRequired(PathToAccountLevel);
Directory.GetFiles(filePath, "*.pdf")
Which does indeed add the quotes, but it makes no difference to the outcome.
Also tried (with and without adding quotes):
Directory.GetFiles(#filePath, "*.pdf")
I've tried as many combinations as I can think of using quotes and '#' etc. but absolutely nothing has worked so far. I've even hard coded the full path down to the Email level and still no joy.
I'm using C# with VS2019 on Windows 10. The server is running Windows Server 2012 R2 DataCenter. I've run the test executable locally on the server and the results are the same.
The really galling aspect of this is that a simple test program written in delphi and using FindFirst/Next works perfectly!
I should also mention that permissions are not an issue, and the files exist.
Any help at all would be great, thanks.
Re the possible duplicate/proposed answer from qaabaam:
Answer 1 for that question is not valid code - the line
DirectoryInfo wantedDir = tempWithoutMac.GetDirectories.Where(c => c.Contains(MacID)).First();
throws an error for GetDirectories being a method and not valid in the context. If you change it to GetDirectories() it then complains that DirectoryInfo does not have a definition for Contains.
Answer 2 using alt 0160 still throws the can't find part of the path error.
Answer 3 is basically an opinion and doesn't help at all.
If you can figure out how to get Answer 1 to work I'll definitely give it a try!
Leading or trailing spaces in directory names is one of those grey areas in Windows: you can do it, but you probably shouldn't. The reason for this is simple - the vast majority of the time, a leading or trailing space is a typo, and the Windows APIs around paths have been designed to take this into account, to the point where (as you've discovered) they will outright ignore leading or trailing spaces. (For example, in Windows 10, it's literally impossible to delete a directory with trailing spaces via Windows Explorer!) Since C# is built on top of these APIs, it inherits their "quirks".
Fortunately there is a way to tell Windows "don't try to fix this path, it is correct, I know what I'm doing" via the use of DOS device paths. In brief:
For local paths, prefix them with the literal \\?\. In other words, C:\foo\bar would become \\?\C:\foo\bar.
For UNC paths, replace the leading \\ with \\?\UNC\. In other words, \\server\foo\bar would become \\?\UNC\server\foo\bar.
In your case, then, all you need to do is change \\MyServer\C$\TopLevel\Account \ to \\?\UNC\MyServer\C$\TopLevel\Account \ and everything should work as you expect.
As an aside, what you are doing is a document search/query operation, which will be incredibly slow as it's performing I/O, and over the network at that. You'd be far better served in writing a tool to migrate these documents into a proper database, then searching that database for the documents.

C# Path.Combine for virtual paths?

I have a .NET MVC application where I am building a virtual path using the below:
Path.Combine("~/Documents/", "application", "username");
The output from this is "~/Documents/application\\username". This causes an error when passed to a Telerik RadFileExplorer however if I manually build the path as "~/Documents/application/username" it works fine. The example above uses strings but the actual code is using variables.
I'd prefer not to use String.Replace if possible... Is there an alternative to Path.Combine that would produce this output? "~/Documents/application/username"
Path.Combine adds the / for you, so you don't need to add it when combining the path.
Try like this: Path.Combine("~/Documents", "application", "username");
There are a lot of alternatives for combining the a path (which is basically a string).
you can use the $ - string interpolation and put the parameters like this: $"~/{Documents}/{application}/{username}" which is more readable than replace.
edit: just saw Raziel's answer, which is probably much better/simpler..
Livio's comment is in general incorrect. That's only true if run on a Unix-based system. If run on a Windows system Path.Combine defaults to backslashes unless otherwise specified.
From the documentation of Path.Combine it only uses the forward slash if it's explicitly included in the path component. I would suggest writing a function for the latter two arguments that just appends a forward slash to the end (and maybe also ensures that there are no slashes at the beginning because that would cause them to be interpreted as absolute paths and overwrite everything before that).

Are backslashes at the end of folder paths relevant?

I am writing a program that copies all files and directories from a source location into a target location. Both locations are provided by the user.
I was just thinking about checking whether the location paths have a backslash "\" at the end, when I decided to run some tests and from what I see it doesn't matter if you tell c#
File.Copy("C:\\test", "D:\\test")`
or
File.Copy("C:\\test\\", "D:\\test\\")`
Am I wrong? Does a backslash at the end of a file path matter?
Update:
On Windows, you apparently cannot name a file and folder the same, so it won't matter there. You should still use a (back)slash for good style and cross-platform compatability. Some software or libraries might even depend on it.
Original Answer:
A folder is generally ending in \ (or /), because there might be a file with no extension, named test. For example, these two could coexist (Linux [notice the different capitalization]):
Folder: /Users/nikxda/Docouments/Test/
File: /Users/nikxda/Docouments/test
If you work case-sensitive, then you could in fact ignore it on Windows. I'd still recommend using a trailing slash, just for
Clarification (specify it is a folder)
Good style (following conventions)
Compatibility (other software, libraries, etc. might depend on it)
Cross platform (OSX, ...)
So yes, you should always use a (back)slash at the end of your path if referring to folders.
It should make no difference, because - on windows - you cannot have a folder "test" and file named "test" without extension in the same parent directory at the same time.
However, it will be much more cleaner to have a trailing backslash within your code, because this leaves no room for speculation. And it will be better for cross-plattform purposes as well.
Thinking of URIs for example, it can definitely make a difference between a trailing slash or no trailing slash, by the way.
TLDR: Use a trailing (back)slash, please :-)

I'm writing C# code that has to run on windows and linux (under mono). Easy way to handle file paths on both?

I'm writing a mod for Kerbal Space Program that logs data to a text file for use in outside tools (matlab, etc). KSP works on both Linux and Windows and I would like my mod to play nice on both. I was kind of hoping that the mono implementation on linux would just do the smart thing and translate the \'s to /'s, especially since I'm just working with Directory.GetCurrentDirectory as my base, so I don't have to worry about detecting/specifying things like c:\ vs /.
An acceptable answer (at least for now) would be a decent way to determine which platform I'm running on and just generate the strings differently (use a seperator char variable that I can set instead of slash string literals). I could look that up myself though, I'm kind of hoping there's a slick solution. I tried Googling/searching on here but nothing really stood out.
Use Path.PathSeparator. \ on Windows, / on Unix.
If you're looking to combine directory names, you can use Path.Combine.
To get the root directory (i.e. / or C:\, you can use Path.GetPathRoot(Directory.GetCurrentDirectory())).
More info in the Path docs.
Mono provides IOMap to convert pathnames, so you can use backslashes happily in your code and then use MONO_IOMAP=all mono something.exe to start your program. Mono has a page with suggestions on application portability.
Use the System.Path.* as much as you can. It will avoid you from having to do specific OS checks to determine temp dirs, app locations, data/Desktop/Doc locations...
And use Path.Combine and stay away from having to using path separators. If you are hand parsing and building paths using Path.PathSeparator, ask your why? The odds are there is a std framework method that does what you want. KISS ;-)

Is it worth it to lookup the default application in the registry when opening a file from a C# application?

I'm building an application (a side project which is likely to enlist the help of the stackoverflow community on more than one occasion) which will need to open a variety of file types (i.e. open Word documents in Word, not natively in my application).
I've been playing with some code for looking up the default application for the file type in the registry and passing this to Process.Start(). There seem to be two issues with this approach:
1) The application name is quoted in some instances, and not in others.
2) Process.Start() requires that the application path and it's arguments are passed separately (i.e. Process.Start("notepad.exe", #"C:\myfile.txt"); rather than Process.Start(#"notepad.exe C:\myfile.txt");).
This means when I retrieve the path from the registry, I have to split it (after determining if I need to split on quotes or spaces) to determine what part is the application path and what parts are arguments, then pass those separately to Process.Start().
The alternative seems to be to just pass the filename, as in Process.Start(#"C:\myfile.txt"), but I think this only works if the application is in the Path environment variable.
Which way is better? In the case of the registry, is there a common solution for how to do the argument parsing?
Thanks for any and all help!
Update:
I guess the short answer is 'No.'
It seems like I was really going the overkill route, and that passing just the filename will work whenever there's an associated value in the registry. I.e. anything I find in the registry myself, Process.Start() already knows how to do.
I did discover that when I try this with a "new" filetype, I get a Win32Exception stating "No application is associated with the specified file for this operation." Fredrik Mörk mentions in a comment that this doesn't occur for him in Vista. What's the proper way to handle this?
If the extension is registered to be opened with a certain application, it doesn't need to be in the PATH in order to run.
The application does not need to be in the PATH if you only specify the filename. The following code worked fine for me:
System.Diagnostics.Process.Start(#"C:\Users\Dan\Desktop\minors.pdf");
You typically do not need to lookup the program for registered types, and the program does not typically need to be in the PATH environment variable. Usually the command in the registry contains the full path. This is how the command for .kml files (Google Earth) looks (in my computer):
C:\Program Files\Google\Google Earth\googleearth.exe "%1"
Given that, you can safely just use Process.Start together with the document file names. Should it be that the file type is not registered you will invoke the default Windows behaviour for this (asking you which program to use, and so on).

Categories

Resources