See if file path is inside a directory - c#

How can I check whether a path to a file that doesn't necessarily exists points to a location inside a particular directory? Say I have a method:
bool IsInside(string path, string folder)
{
//...
}
Then, if I call it like:
IsInside("C:\\Users\\Dude\\Hi", "C:\\Users\\Dude\\Hi\\SubFolder\\SubSubFolder\\tile.txt")
should return true (note the sub folder), but if I call it like:
IsInside("C:\\Users\\Dude\\Hi", "C:\\Users\\Dude\\BadFolder\\SubFolder\\SubSubFolder\\tile.txt")
should return false. The only thing I can think of right now is using string's StartsWith, but sounds kinda hacky to me. I haven't found a native .NET method that would check this either.

Do you need to handle relative paths (../../someFile.txt)? Something like this would work:
private bool IsInside(DirectoryInfo path, DirectoryInfo folder)
{
if (path.Parent == null)
{
return false;
}
if (String.Equals(path.Parent.FullName, folder.FullName, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
return IsInside(path.Parent, folder);
}
Then call it like this:
DirectoryInfo folder = new DirectoryInfo("C:\\Users\\Dude\\Hi");
DirectoryInfo path = new DirectoryInfo("C:\\Users\\Dude\\Hi\\SubFolder\\SubSubFolder\\tile.txt");
bool result = IsInside(path, folder);

You could try the string.IndexOf method. If you use the overload with the StringComparison enumeration it should give you the result you need.
From above link:
Reports the zero-based index of the first occurrence of one or more characters, or the first occurrence of a string, within this string. The method returns -1 if the character or string is not found in this instance.
bool IsInside(string folder, string path)
{
if (path.IndexOf(folder,StringComparison.OrdinalIgnoreCase) != -1)
return true;
else
return false;

Related

C# - Using File.Exists, when file DOESN'T exist does not run the last else statement

Super new to C#. I'm having an input get split and then find an ID from the pointsTarget var.
When the file DOES exist, it seems that the line
else if (File.Exists(filePath+userId+txt))
returns true;
because it runs just fine and sets the argument "addPointsCompleted" to TRUE. It works just how I would expect. But when the file does NOT exist, I want it to return false and run the last else statement:
CPH.SetArgument("missing", "True");
and set "missing" to TRUE.
I feel like there is something wrong with the way I put in the if/else if/else statement because I get an error :
"System.IO.FileNotFoundException: Could not find file 'E:\Users\Troy\Documents\Stuff\PointNames\Test.txt'.
using System;
using System.IO;
public class CPHInline
{
public bool Execute()
{
string rawInput = args["rawInput"].ToString();
string[] split= rawInput.Split('+');
var pointsTarget = split[0].ToString();
var addPoints = split[1].ToString();
CPH.LogInfo($"pointsTarget is {pointsTarget}");
CPH.LogInfo($"addPoints is {addPoints}");
var user = args["user"].ToString();
CPH.SetArgument("pointsTarget", pointsTarget);
string userPath = #"E:/Users/Troy/Documents/Stuff/PointNames/";
string filePath = #"E:/Users/Troy/Documents/Stuff/PointIDs/";
string txt = ".txt";
var userId = File.ReadAllText(userPath+pointsTarget+txt);
CPH.LogInfo($"userId is {userId}");
if (user == pointsTarget)
{
CPH.SetArgument("corrupt", "True");
}
else if (File.Exists(filePath+userId+txt))
{
//DO THIS
string fileName = filePath+userId+txt;
string points = File.ReadAllText(fileName);
int x = Convert.ToInt32(points);
int y = Convert.ToInt32(addPoints);
int sum = x + y;
String newPoints;
newPoints = sum.ToString();
File.WriteAllText(fileName, newPoints);
CPH.SetArgument("newPoints", newPoints);
CPH.SetArgument("addPointsCompleted", "True");
}
else
{
//do this
CPH.SetArgument("missing", "True");
}
return true;
}
}
I tried looking around, but all the issues are from people where the file DOES exist and they can't find it. My problem is kind of the opposite.
I feel like there is something wrong with the way I put in the if/else if/else statement because I get an error "System.IO.FileNotFoundException: Could not find file 'E:\Users\Troy\Documents\Stuff\PointNames\Test.txt'.
This is a good opportunity for you to start familiarizing yourself with using a debugger to step through the code and observe its behavior. Because the problem has nothing to do with your if structure. It's happening before your if block. Right here:
var userId = File.ReadAllText(userPath+pointsTarget+txt);
Look at the error message. It's trying to read a file in the "PointNames" folder. Which is in your userPath variable:
string userPath = #"E:/Users/Troy/Documents/Stuff/PointNames/";
Which is only ever used in that one line of code that tries to read a file. And File.ReadAllText will throw a FileNotFoundException if the file is not found.
It seems you're already aware of how to check if a file exists. So why not apply that here? For example:
var userId = string.Empty;
if (File.Exists(userPath+pointsTarget+txt))
{
userId = File.ReadAllText(userPath+pointsTarget+txt);
}
else
{
// handle the error in some way
}

How to validate if a folder already exists

I want to create folders and add classes within the folders. I can create the folder once, but once I have created it, I just want to add classes. My code gives error because I'm trying to create a folder several times, which is not right. So before adding a class to the folder, I want to check if the folder already exists and if it exists I just want to add the class.
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
According to documentation, there is no kind of Exists function which would tell us if a folder already existed.
So you have at least two options:
1. Try and ignore
Simply:
try
{
var rootFolder = project.ProjectItems.AddFolder(folder);
}
catch
{
/* folder already exists, nothing to do */
}
2. Solution folders can only occur in the first level below the solution root node, so we can get away with a non-recursive solution.
public static bool CheckIfSolutionFolderExists(ProjectItems projectItems, string foldername)
{
foreach(var projectItem in projectItems)
{
if(projectItem.Kind == EnvDTE.vsProjectItemKindVirtualFolder)
{
if(projectItem.Name == foldername)
{
return true;
}
}
}
return false;
}
For a recursive solution, I found this, which boils down to:
public static bool CheckIfFileExistsInProject(ProjectItems projectItems, string fullpath)
{
foreach(ProjectItem projectItem in projectItems)
{
if(projectItem.Name == fullpath)
{
return true;
}
else if ((projectItem.ProjectItems != null) && (projectItem.ProjectItems.Count > 0))
{
/* recursive search */
return CheckIfFileExistsInProject(projectItem.ProjectItems, fullpath);
}
}
return false;
}
A robust pattern for solution and project folder management would use AddFromDirectory whenever you want to mirror the file system hierarchy in the project tree, and AddFolder only for virtual folders that have no representation in the file system.
ProjectItem item;
if(Directory.Exists(itemname))
{
item = project.AddFromDirectory(itemname);
}
else
{
item = project.AddFolder(itemname);
}
(source: inspired from this)
You can use method Directory.Exists
and in your code it'll be look like there
if(!Directory.Exists(folder)) {
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
}
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
You need to do the following:
if(Directory.Exists(path))
{
// The folder already exists
}
else
{
//Create a new folder here ...
}
Please check this out for more details on Directory.Exists

Delete file from folder where contains

I am looking to create a Method which I can use to delete files from a directory when the filename contains the wildcard parameter.
So far I have the below but I cannot seem to work out how I can delete any files from my fileEntries collection that contain the wildcard.
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
// Process the list of ALL files found in the directory.
string[] fileEntries = Directory.GetFiles(targetDirectory);
foreach (var item in fileEntries)
{
var itemToDelete = fileEntries.Contains(wildcard);
// delete where contains
}
}
Can anyone finish this or suggest a more efficient way of doing this in one neat method call which takes the directory and wildcard?
You can use the search-pattern of GetFiles/EnumerateFiles(which can be more efficient):
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
string searchPattern = string.Format("*{0}*", wildcard);
var filesToDelete = Directory.EnumerateFiles(targetDirectory, searchPattern);
foreach (var fileToDelete in filesToDelete)
{
try{
File.Delete(fileToDelete);
}catch(Exception ex){
// log this...
}
}
}
Look at the remarks section for further information.
First off, this line is wrong:
var itemToDelete = fileEntries.Contains(wildcard);
This returns a boolean indicating whether at least one of the filenames is an exact match. What you want is:
var items = fileEntries.Where(name => name.Contains(wildcard));
Second, you don't even need to filter the filenames like this, you can simply use this other GetFiles overload that takes a search pattern.
Finally, you can then use File.Delete to actually delete those files.
This is my first stab at something like this with LINQ, but this worked for me:
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
Directory.GetFiles(targetDirectory).Where(j => j.Contains(wildcard)).ToList().ForEach(i => File.Delete(i));
}
Or this uses a multiline lambda expression to include error handling:
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
Directory.GetFiles(targetDirectory).Where(j => j.Contains(wildcard)).ToList().ForEach(i =>
{
try
{
File.Delete(i);
}
catch (Exception ex)
{
//do something here on exception.
}
});
}
I would use the wildcard to get only the files that I am interested in.
public static void DeleteFileContaining(string targetDirectory, string wildcard){
string[] fileEntries = Directory.GetFiles(targetDirectory, wildcard);//something like *.jpg
// delete all matching files.
foreach (string f in fileEntries)
{
File.Delete(f);
}
}
I am not sure you want to delete from folder but as you have mentioned you want to remove from fileEntries collection you can achieve by this
fileEntries = fileEntries.Where(a => !a.Contains(wildcard)).ToArray();
You might want to look at the System.IO-Class to delete files (http://msdn.microsoft.com/en-us/library/System.IO(v=vs.110).aspx)

Removing ../ in the middle of a relative path

I want to get from this
"../lib/../data/myFile.xml"
to this
"../data/myFile.xml"
I guess I could do it by manipulating the string, searching for "../" and canceling them out with the preceding folders but I was looking for an already existing C# solution.
Tried instantiating an Uri from this string and going back toString(). Didn't help. It leaves the string unchanged.
You can always try to use:
Path.GetFullPath("../lib/../data/myFile.xml")
It behaves as you want with absolute paths but you might end up with strange behaviors with relative paths since it always bases itself from the current working directory. For instance:
Path.GetFullPath("/lib/../data/myFile.xml") // C:\data\myFile.xml
Path.GetFullPath("../lib/../data/myFile.xml") // C:\Program Files (x86)\data\myFile.xml
Sounds like you may either need to parse/rebuild the path yourself, or use some kind of well constructed regular expression to do this for you.
Taking the parse/rebuild route, you could do something like:
public static string NormalisePath(string path)
{
var components = path.Split(new Char[] {'/'});
var retval = new Stack<string>();
foreach (var bit in components)
{
if (bit == "..")
{
if (retval.Any())
{
var popped = retval.Pop();
if (popped == "..")
{
retval.Push(popped);
retval.Push(bit);
}
}
else
{
retval.Push(bit);
}
}
else
{
retval.Push(bit);
}
}
var final = retval.ToList();
final.Reverse();
return string.Join("/", final.ToArray());
}
(and yes, you'd probably want better variable names/commenting/etc.)
You can use a regular expression to do this:
public static string NormalisePath(string path)
{
return new Regex(#"\.{2}/.*/(?=\.\.)").Replace(path, "");
}

Getting the relative path from the full path

I have to get the path excluding the relative path from the full path,
say
The relative path is ,C:\User\Documents\
fullpath ,C:\User\Documents\Test\Folder2\test.pdf
I want to get only the path after the relative path i.e \Test\Folder2\test.pdf
how can i achieve this.
I am using C# as the programming language
You are not talking about relative, so i will call it partial path.
If you can be sure that the partial path is part of your full path its a simple string manipulation:
string fullPath = #"C:\User\Documents\Test\Folder2\test.pdf";
string partialPath = #"C:\User\Documents\";
string resultingPath = fullPath.Substring(partialPath.Length);
This needs some error checking though - it will fail when either fullPath or partialPath is null or both paths have the same length.
Hmmmm, but what if the case is different? Or one of the path uses short-names for its folders? The more complete solution would be...
public static string GetRelativePath(string fullPath, string containingFolder,
bool mustBeInContainingFolder = false)
{
var file = new Uri(fullPath);
if (containingFolder[containingFolder.Length - 1] != Path.DirectorySeparatorChar)
containingFolder += Path.DirectorySeparatorChar;
var folder = new Uri(containingFolder); // Must end in a slash to indicate folder
var relativePath =
Uri.UnescapeDataString(
folder.MakeRelativeUri(file)
.ToString()
.Replace('/', Path.DirectorySeparatorChar)
);
if (mustBeInContainingFolder && relativePath.IndexOf("..") == 0)
return null;
return relativePath;
}
To expand on Jan's answer, you could create an extension method on the string class (or the Path class if you wanted) such as:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static string GetPartialPath(this string fullPath, string partialPath)
{
return fullPath.Substring(partialPath.Length)
}
}
}
And then use:
using ExtensionMethods;
string resultingPath = string.GetPartialPath(partialPath);
I haven't tested that this extension method works, but it should do.

Categories

Resources