What is a good regular expression that can validate a text string to make sure it is a valid Windows filename? (AKA not have \/:*?"<>| characters).
I'd like to use it like the following:
// Return true if string is invalid.
if (Regex.IsMatch(szFileName, "<your regex string>"))
{
// Tell user to reformat their filename.
}
As answered already, GetInvalidFileNameChars should do it for you, and you don't even need the overhead of regular expressions:
if (proposedFilename.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) != -1)
{
MessageBox.Show("The filename is invalid");
return;
}
This isn't as simple as just checking whether the file name contains any of System.IO.Path.GetInvalidFileNameChars (as mentioned in a couple of other answers already).
For example what if somebody enters a name that contains no invalid chars but is 300 characters long (i.e. greater than MAX_PATH) - this won't work with any of the .NET file APIs, and only has limited support in the rest of windows using the \?\ path syntax. You need context as to how long the rest of the path is to determine how long the file name can be. You can find more information about this type of thing here.
Ultimately all your checks can reliably do is prove that a file name is not valid, or give you a reasonable estimate as to whether it is valid. It's virtually impossible to prove that the file name is valid without actually trying to use it. (And even then you have issues like what if it already exists? It may be a valid file name, but is it valid in your scenario to have a duplicate name?)
Why not using the System.IO.FileInfo class, together with the DirectoryInfo class you have a set of usefull methods.
Path.GetInvalidFileNameChars - Is not a good way. Try this:
if(#"C:\A.txt".IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) != -1)
{
MessageBox.Show("The filename is invalid");
return;
}
Related
I know several similar questions were asked in the past and I also know I could use Directory.Exists() or File.Exists() or to check the file system using API calls but I'm trying to make this decision simply based on an input string.
public bool ValidateOutputFilename ( string sPath )
{
// check if sPath is actually a filename
}
My guess is that it's not possible because something that looks like a folder name (no extension but no trailing \) may actually be a file (for example C:\A\B\C may represent a file or a folder and vice versa).
The reason I want to avoid a file system check is because the path may / may not exist and sPath may represent a network location in which case the file system query will be slow.
I'm hoping that someone can recommend an idea I haven't already considered.
I think you can't avoid a file system call.
Only the file system knows for sure.
As you have said, a simple, well formatted, string is unidentifiable as a path or file.
A way to answer to your question is through the File.GetAttributes method.
It returns a FileAttributes enum value that can be tested with a bitwise AND to find if the Directory bit is set and is the fastest method (apart from a direct unmanaged call).
try
{
// get the file attributes for file or directory
FileAttributes attr = File.GetAttributes(sPath);
bool isDir = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? true : false;
if (isDir == false)
....
else
....
}
}
catch(Exception ex)
{
// here as an example. probably you should handle this in the calling code
MessageBox.Show("GetAttributes", ex.Message);
}
Of course, if the file or directory represented by the path not exist you get an exception that should be handled.
As a side note: Directory.Exists or File.Exists could tell you if a file or directory exists with the specified name, but how do you call the correct one if you don't know what your path string represents? You need to call both to be sure.
There is no way to get more information about a file unless you physically read it. From what I understand, you want to avoid reading the file.
You have no other option but to verify extensions and trailing slashes contained in the string. But even like that, the result will never be real. For example, I just created this folder in my d:
D:\Music\file.txt
and I created this file inside:
D:\Music\file.txt\folder
I have a FileUpload control, and there are certain restrictions on the file name, certain characters that shouldn't be allowed. The following code works for most characters, but for some reason not others:
if (FileUpload1.HasFile)
{
if (FileUpload1.FileName.Contains('#') ||
FileUpload1.FileName.Contains('&') ||
FileUpload1.FileName.Contains(';') ||
FileUpload1.FileName.Contains('{') ||
FileUpload1.FileName.Contains('}') ||
FileUpload1.FileName.Contains('+'))
{
//error: bad character detected
}
}
I could probably do this another (better) way, with a regular expression, but first I really want to know why the above doesn't work.
The following characters are detected in the FileName:
# & } +
The following characters are not detected in the FileName:
; {
Why?
Edit: examples of file names that I've tried.
Final+Version.pdf //+ detected
Final;Version.pdf //; NOT detected
WhyHello{there.pdf //{ NOT detected
Policies}20120303.pdf //} detected
As mentioned in the comments below, there is no problem with these characters on strings, so maybe it's a problem with the value of "FileName" or the way the FileUploader handles the file name?
Edit 2:
Breakpoint step through shows that, using Policies{20120303.pdf as an example, the value of FileName is Policies.pdf. So this is not a problem with .Contains() anymore, but with FileUpload and FileName.
So the new question is, how can I handle this? I don't want files with these characters to go through, and I don't want the submitted files to be named differently from what the user named them. So if someone tries to submit 'Policies{20120303.pdf', I need one of two results:
invalid name is detected, procedure aborted
submit the file with the complete and original name, Policies{20120303.pdf
Edit 3:
If I submit a file with the following name: foo;bar{baz.txt, the value of FileUpload.FileName is "foo.txt"
Edit 4:
Thanks to some helpful comments below, I tried using a different browser (Chrome), and it works just fine! The file name stays intact, even with foo;bar{baz.txt. I use Opera, and it wasn't working. I guess that narrows it down quite a lot to a browser specific issue. I don't think there's gonna be any way to make this work properly in Opera, unless someone has any ideas?
I used the following code in my program to check the filename:
filename = txtFileName.Text;
if (filename == "" || (filename.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) != -1))
{
// Ask for a new file name, or whatever else you need to do.
}
In my case, however, I'm checking the user provided string before setting the file name in the file object. I think that would probably solve your problem as well.
I'm guessing that somewhere there's a function that alters the string before you get to the point of verifying it.
I use this code for finding the debug directory
public string str_directory = Environment.CurrentDirectory.ToString();
"C:\\Users\\Masoud\\Documents\\Visual Studio 2008\\Projects\\MyProj\\MyProj\\bin\\Debug"
How can I find the parent folder as shown below?
"C:\\Users\\Masoud\\Documents\\Visual Studio 2008\\Projects\\MyProj\\MyProj"
You can use System.IO.Directory.GetParent() to retrieve the parent directory of a given directory.
string parent = System.IO.Directory.GetParent(str_directory).FullName;
See BOL
If you append ..\.. to your existing path, the operating system will correctly browse the grand-parent folder.
That should do the job:
System.IO.Path.Combine("C:\\Users\\Masoud\\Documents\\Visual Studio 2008\\Projects\\MyProj\\MyProj\\bin\\Debug", #"..\..");
If you browse that path, you will browse the grand-parent directory.
Edit: The normalization covered in this answer only happens when the path is used to access the file system, but not on the string itself. By contrast, this answer achieves the result and normalization purely using path strings, without using the file system at all.
I've found variants of System.IO.Path.Combine(myPath, "..") to be the easiest and most reliable. Even more so if what northben says is true, that GetParent requires an extra call if there is a trailing slash. That, to me, is unreliable.
Path.Combine makes sure you never go wrong with slashes.
.. behaves exactly like it does everywhere else in Windows. You can add any number of \.. to a path in cmd or explorer and it will behave exactly as I describe below.
Some basic .. behavior:
If there is a file name, .. will chop that off:
Path.Combine(#"D:\Grandparent\Parent\Child.txt", "..") => D:\Grandparent\Parent\
If the path is a directory, .. will move up a level:
Path.Combine(#"D:\Grandparent\Parent\", "..") => D:\Grandparent\
..\.. follows the same rules, twice in a row:
Path.Combine(#"D:\Grandparent\Parent\Child.txt", #"..\..") => D:\Grandparent\
Path.Combine(#"D:\Grandparent\Parent\", #"..\..") => D:\
And this has the exact same effect:
Path.Combine(#"D:\Grandparent\Parent\Child.txt", "..", "..") => D:\Grandparent\
Path.Combine(#"D:\Grandparent\Parent\", "..", "..") => D:\
To get a 'grandparent' directory, call Directory.GetParent() twice:
var gparent = Directory.GetParent(Directory.GetParent(str_directory).ToString());
Directory.GetParent is probably a better answer, but for completeness there's a different method that takes string and returns string: Path.GetDirectoryName.
string parent = System.IO.Path.GetDirectoryName(str_directory);
Like this:
System.IO.DirectoryInfo myDirectory = new DirectoryInfo(Environment.CurrentDirectory);
string parentDirectory = myDirectory.Parent.FullName;
Good luck!
No one has provided a solution that would work cross-form. I know it wasn't specifically asked but I am working in a linux environment where most of the solutions (as at the time I post this) would provide an error.
Hardcoding path separators (as well as other things) will give an error in anything but Windows systems.
In my original solution I used:
char filesep = Path.DirectorySeparatorChar;
string datapath = $"..{filesep}..{filesep}";
However after seeing some of the answers here I adjusted it to be:
string datapath = Directory.GetParent(Directory.GetParent(Directory.GetCurrentDirectory()).FullName).FullName;
You might want to look into the DirectoryInfo.Parent property.
IO.Path.GetFullPath(#"..\..")
If you clear the "bin\Debug\" in the Project properties -> Build -> Output path, then you can just use AppDomain.CurrentDomain.BaseDirectory
Since nothing else I have found helps to solve this in a truly normalized way, here is another answer.
Note that some answers to similar questions try to use the Uri type, but that struggles with trailing slashes vs. no trailing slashes too.
My other answer on this page works for operations that put the file system to work, but if we want to have the resolved path right now (such as for comparison reasons), without going through the file system, C:/Temp/.. and C:/ would be considered different. Without going through the file system, navigating in that manner does not provide us with a normalized, properly comparable path.
What can we do?
We will build on the following discovery:
Path.GetDirectoryName(path + "/") ?? "" will reliably give us a directory path without a trailing slash.
Adding a slash (as string, not as char) will treat a null path the same as it treats "".
GetDirectoryName will refrain from discarding the last path component thanks to the added slash.
GetDirectoryName will normalize slashes and navigational dots.
This includes the removal of any trailing slashes.
This includes collapsing .. by navigating up.
GetDirectoryName will return null for an empty path, which we coalesce to "".
How do we use this?
First, normalize the input path:
dirPath = Path.GetDirectoryName(dirPath + "/") ?? ""; // To handle nulls, we append "/" rather than '/'
Then, we can get the parent directory, and we can repeat this operation any number of times to navigate further up:
// This is reliable if path results from this or the previous operation
path = Path.GetDirectoryName(path);
Note that we have never touched the file system. No part of the path needs to exist, as it would if we had used DirectoryInfo.
To avoid issues with trailing \, call it this way:
string ParentFolder = Directory.GetParent( folder.Trim('\\')).FullName;
To get your solution
try this
string directory = System.IO.Directory.GetParent(System.IO.Directory.GetParent(Environment.CurrentDirectory).ToString()).ToString();
This is the most common way -- it really depends on what you are doing exactly:
(To explain, the example below will remove the last 10 characters which is what you asked for, however if there are some business rules that are driving your need to find a specific location you should use those to retrieve the directory location, not find the location of something else and modify it.)
// remove last 10 characters from a string
str_directory = str_directory.Substring(0,str_directory.Length-10);
You shouldn't try to do that. Environment.CurrentDirectory gives you the path of the executable directory. This is consistent regardless of where the .exe file is. You shouldn't try to access a file that is assumed to be in a backwards relative location
I would suggest you move whatever resource you want to access into a local location. Of a system directory (such as AppData)
In my case I am using .NET 6. When I use:
public string str_directory = Environment.CurrentDirectory.ToString();
returns C:\Projects\MyTestProject\bin\Debug\net6.0
In order to reach C:\Projects\MyTestProject I am using the following:
Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent
You can chain Parent to Directory.GetParent(Environment.CurrentDirectory) in order to reach the directory you want.
Final version:
public string str_directory = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.ToString();
What I mean by this question is, when you need to store or pass a URL around, using a string is probably a bad practice, and a better approach would be to use a URI type. However it is so easy to make complex things more complex and bloated.
So if I am going to be writing to a file on disk, do I pass it a string, as the file name and file path, or is there a better type that will be better suited to the requirement?
This code seems to be clunky, and error prone? I would also need to do a whole bit of checking if it is a valid file name, if the string contains data and the list goes on.
private void SaveFile(string fileNameAndPath)
{
//The normal stuff to save the file
}
A string is fine for the filename. The .Net Framework uses strings for filenames, that seems fine.
To validate the filename you could try and use a regular expression or check for invalid characters against System.IO.Path.GetInvalidFileNameChars. However, it is probably easier to handle the exceptional cases of invalid filenames by handling the exception that will occur when you try and create the file - plus you need to do this anyway....
Unfortunate as it is, string is the idiomatic way of doing this in .NET - if you look at things like FileStream constructors etc, they use strings.
You could consider using FileInfo (or DirectoryInfo) but that would be somewhat unusual.
You could use FileInfo (from System.IO) to pass it around, but strings are more or less standard when referring to files.
You could always use Path.GetFileName({yourstring}) to get the filename from the path.
String is fine, but you should put in some effort to ensure that the file is being saved into the directory you expect.
If you're working with a filepath a string is usual.
If you're working with a URL you could consider using the System.Uri class e.g.
Uri myUri = new Uri(myUrl, UriKind.Absolute);
This will allow you to work with properties such as uri.Host, uri.Absolute path etc. It will also give you a string array (Segments) for the separate subfolders in the url.
MSDN info here: System.Uri
How to validate by a single regular expression the urls:
http://83.222.4.42:8880/listen.pls
http://www.my_site.com/listen.pls
http://www.my.site.com/listen.pls
to be true?
I see that I formulated the question not exactly :(, sorry my mistake. The idea is that I want to validate with the help of regexp valid urls, let it be an external ip address or the domain name. This is the idea, other valid urls can be considered:
http://93.122.34.342/
http://193.122.34.342/abc/1.html
http://www.my_site.com/listen2.pls
http://www.my.site.com/listen.php
and so on.
The road to hell is paved with string parsing.
URL parsing in particular is the source of many, many exploited security issues. Don't do it.
For example, do you want this to match?
Note the uppercase scheme section. Remember that some parts of a URL are case sensitive, and some are not. Then there's encoding rules. Etc.
Start by using System.Uri to parse the URLs you provide:
var uri = new Uri("http://83.222.4.42:8880/listen.pls");
Then you can write things like:
if (uri.Scheme == "http" &&
uri.Host == "83.222.4.42" &&
uri.AbsolutePath == "/listen.pls"
)
{
// ...
}
^http://.+/listen\.pls$
If there are strictly only 3 of them don't bother with a regular expression because there is not necessarily a good pattern match when everything is already strictly known - in fact you might accidentally match more than these three urls - which becomes a problem if the urls are intended for security purposes or something equally important. Instead, test the three cases directly - maybe put them in a configuration file.
In the future if you want to add more URLs to the list you'll likely end up with an overly complicated regular expression that's increasingly hard to maintain and takes the place of a simpler check against a small list.
You won't necessarily get speed gains by running Regex to find these three strings - in fact it might be quite expensive.
Note: If you wantUri regular expressions also try websites hosting libraries like Regex Library - there are many to pick and choose from if your needs change.
/^http:\/\/[-_a-zA-Z0-9.]+(:\d+)?\/listen\.pls$/
Do you mean any URL ending with /listen.pls? In that case try this:
^http://[^/]+/listen\.pls$
or if the protocol identifier must be optional:
^[http://]?[^/]+/listen\.pls$
Anyway take a look here, maybe it is useful for you: Url and Email validation using Regex
A modified version base upon Jay Bazuzi's solution above since I can't post code in comment, it checks a blacklisted extensions (I do this only for demonstration purpose, you should strongly consider to build a whitelist rather than a blacklist) :
string myurl = "http://www.my_site.com/listen.pls";
Uri myUri = new Uri(myurl);
string[] invalidExtensions = {
".pls",
".abc"
};
foreach(string invalidExtension in invalidExtensions) {
if (invalidExtension.ToLower().Equals(System.IO.Path.GetExtension(myUri.AbsolutePath))) {
//Logic here
}
}