I am using Visual Studio 2013 Express and I am new to Visual C#. I am sure there is a better way to do what I am trying and I would appreciate any suggestions.
The code I'm trying to write evaluates a series of tests and sets a flag only if all tests = TRUE. I'm currently using six nested if structures to get this done and, while it works, I'm looking for a cleaner, more professional solution. Here's the sample (shortened to three levels for this post):
private const string sDrive = "D:\\";
private const string sFolder = "FTP\\";
private const string sDivFolder = "ABC";
private static bool bSanity = false;
private static void SanityCheck()
{
if (Directory.Exists(sDrive))
{
if (Directory.Exists(sDrive + sFolder))
{
if (Directory.Exists(sDrive + sFolder + sDivFolder))
{
bSanity = true;
}
else
{
Console.WriteLine("FATAL: Div folder doesn't exist.");
}
}
else
{
Console.WriteLine("FATAL: Root folder doesn't exist.");
}
}
else
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
}
The main issue is whether you need to keep the same error reporting you have now. In general, if that is required, I think this is simpler to handle by inverting the cases. That will allow you to remove the nesting by using if/else if:
private static void SanityCheck()
{
if (!Directory.Exists(sDrive))
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
else if (!Directory.Exists(Path.Combine(sDrive, sFolder))
{
Console.WriteLine("FATAL: Root folder doesn't exist.");
}
else if (!Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
{
Console.WriteLine("FATAL: Div folder doesn't exist.");
}
else
{
bSanity = true;
}
}
If the detailed error reporting is not required, you can just check for the lowest level folder directly:
private static void SanityCheck()
{
if (Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
bSanity = true;
else
Console.WriteLine("FATAL: Drive or folder doesn't exist.");
}
if (Directory.Exists(sDrive) && Directory.Exists(sDrive + sFolder) && Directory.Exists(sDrive + sFolder + sDivFolder))
{
bSanity = true;
}
else
{
Console.WriteLine("FATAL: Disk drive doesn't exist.");
}
&& is an early exit operator.
How about using a loop and an array?
// set path array
var paths = new[] { sDrive, sFolder, sDivFolder };
// use StringBuilder for faster string concatenation
var sb = new StringBuilder();
foreach (var p in paths)
{
// append next part of the path
sb.Append(p);
// check if it exists
if (!Directory.Exists(sb.ToString()))
{
// print info message and return from method, because path is incorrect
Console.WriteLine("FATAL: \"{0}\" path doesn't exist.", sb.ToString());
return;
}
}
// we are here, so the whole path works and we can set bSanity to true
bSanity = true;
You can easily manipulate how deap the check is by changing array length. And it will print you exactly what part of the path is not correct.
So one possible cleaner solution might be this:
private static List<Tuple<string, string>> _dir = new List<Tuple<string, string>>
{
Tuple.Create(#"D:\", "FATAL: Disk drive doesn't exist."),
Tuple.Create("FTP", "FATAL: Root folder doesn't exist."),
Tuple.Create("ABC", "FATAL: Div folder doesn't exist."),
}
private static void SanityCheck()
{
var path = string.Empty;
foreach (var t in _dir)
{
path = Path.Combine(path, t.Item1);
if (!Directory.Exists(path))
{
Console.WriteLine(t.Item2);
break;
}
}
}
This isn't exactly the same behaviour as the original, but it is much simpler.
if (Directory.Exists(Path.Combine(sDrive, sFolder, sDivFolder))
bSanity = true;
else
Console.WriteLine("FATAL: Div folder doesn't exist.");
Related
I have a requirement where I want to get the Environment.SpecialFolder value from a file path.
Eg -
string filePath = #"C:\Program Files (x86)\text.txt"
//SpecialFolder sf = GetSpecialFolderAssociatedWithPath(filePath); // sf will be ProgramFilesX86
//Need something similar
I want to further use the sf to generate another path, so if we get the path corresponding to that particular SpecialFolder, that will work as well.
You could do it something like this (assuming you want to get the actual enum value for the special folder):
public static Environment.SpecialFolder? FindSpecialFolder(string filePath)
{
filePath = Path.GetFullPath(filePath);
foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
{
string directory = Environment.GetFolderPath(folder);
if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
return folder;
}
return null;
}
Note that I had to return a nullable because Microsoft failed to follow their own guidelines and didn't include a special "None" zero value in the Environment.SpecialFolder enum that I could return to indicate "not found".
The usage would be something like this:
string filePath = #"C:\Program Files (x86)\text.txt";
var folder = FindSpecialFolder(filePath);
if (folder == null)
Console.WriteLine("No folder found");
else
Console.WriteLine(folder.Value);
If you want both the path and the enum value, you could return them both in a tuple:
public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
filePath = Path.GetFullPath(filePath);
foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
{
string directory = Environment.GetFolderPath(folder);
if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
return (folder, directory);
}
return default;
}
Which you could use like:
var folder = FindSpecialFolder(filePath);
if (folder.specialFolder == null)
Console.WriteLine("No folder found");
else
Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");
Actually, it's possible that some of the special folders may be nested underneath other special folders, for example you might have:
C:\Path1\Path2
C:\Path1\Path2\Path3
In that event, the code above will return the first path that it matches, rather than the longest; i.e. looking for "C:\Path1\Path2\Path3\SomeFile.txt" might return the special folder for "C:\Path1\Path2" rather than the one for "C:\Path1\Path2\Path3".
If you want to handle that possibility, you'll have to find the longest matching path, for example:
public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
filePath = Path.GetFullPath(filePath);
int longest = 0;
Environment.SpecialFolder? longestFolder = null;
string? longestDir = null;
foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
{
string directory = Environment.GetFolderPath(folder);
if (directory.Length > longest && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
{
longestDir = directory;
longestFolder = folder;
longest = directory.Length;
}
}
return (longestFolder, longestDir);
}
And use it like:
var folder = FindSpecialFolder(filePath);
if (folder.specialFolder == null)
Console.WriteLine("No folder found");
else
Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");
Another thing to be aware of is that multiple special folders might have the same path. In this case it's not possible to differentiate them, so the code will just return the first match it finds.
Also note the use of filePath = Path.GetFullPath(filePath); to ensure that relative paths are converted to absolute paths, otherwise the matching likely wouldn't work.
I'm not aware of any existing function to do this, but rolling your own isn't too difficult. For example:
private static bool TryGetSpecialFolderAssociatedWithPath(string filePath, out Environment.SpecialFolder sf)
{
foreach (Environment.SpecialFolder value in Enum.GetValues(typeof(Environment.SpecialFolder)))
{
string path = Environment.GetFolderPath(value);
if (!string.IsNullOrEmpty(path) && filePath.StartsWith(path + "\\"))
{
sf = value;
return true;
}
}
sf = default; // Actually the same as Desktop
return false;
}
Usage:
string filePath = #"C:\Program Files (x86)\text.txt";
if (TryGetSpecialFolderAssociatedWithPath(filePath, out Environment.SpecialFolder sf))
{
Console.WriteLine("Special folder is " + sf);
}
This produces the following output:
Special folder is ProgramFilesX86
One fine point to note is that I append a backslash to the returned path. If I didn't, the code snippet would've hit ProgramFiles before ProgramFilesX86.
I am working with files on C# and I got to a point where I don't know how to continue anymore.
The scenario is this: If I upload 3 or more files with the same name at the same time, I want to handle them and change their name to from "myfile.pdf" to "myfile.pdf(1)/(2)/(3)..." depending on how much files I upload.
This is what I have tried so far and in this case, this only works for only the second file because when the third one comes, it will check there is any file with the same - yes, okay name it "myfile.pdf(2) - but this exists too so it will go to another place.
How can I achieve having the same three files in the same folder with this naming convention?
Here's what I have tried so far:
string FileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\"
if (File.Exists(path))
{
int i = 1;
var FileExists = false;
while (FileExists==false)
{
if (FileExists == false)
{
FileName = FileName + "(" + i + ")";
}
else
return;
i++;
}
}
And the result of this code is: "MyFile.pdf", "MyFile.pdf(1)" And the third one doesn't load here.
I think I'm missing something in the loop or idk :(.
Can someone help me?
I have tried also this:
if(File.Exists(path) || File.Exists(path+"(")
//because when the second file its uploaded, its name will be SecondFile.pdf(1), so this will return true and will proceed running, but still the iteration will "always" start from 0 since everytime I upload a file, I have to refresh the process.
Don't use return inside your while loop, better set 'FileExists = true' whenever you want you loop to stop. A return statement will exit your current method.
I think your problem can be easily solved using recursion, something like this (untested):
public class Program
{
public string FileName { get; set; }
public Program() {
string fileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\";
FileName = CheckFileName(path, fileName);
}
public string CheckFileName(string path, string fileName, int iteration = 0) {
if (File.Exists($"{path}{fileName}")) {
iteration++;
CheckFileName(path, $"{fileName}({iteration})", iteration);
}
return fileName;
}
}
What this does is: it CheckFileName method will keep calling itself until it finds a name that doesn't exist yet.
This should do the job.
public class Program
{
public static string GetUnusedFilePath(string directorypath, string filename, string ext)
{
string fullPath = $"{directorypath}{filename}{ext}";
int inc = 0;
// check until you have a filepath that doesn't exist
while (File.Exists(fullPath))
{
fullPath = $"{directorypath}{filename}{inc}{ext}";
inc++;
}
return fullPath;
}
public static void UploadFile(string filepath)
{
using (FileStream fs = File.Create(filepath))
{
// Add some text to file
Byte[] title = new UTF8Encoding(true).GetBytes("New Text File");
fs.Write(title, 0, title.Length);
}
}
public static void Main()
{
string[] filestoUpload = { "file", "file", "file", "anotherfile", "anotherfile", "anotherfile" };
string directorypath = #"D:\temp\";
string ext = ".txt";
foreach(var file in filestoUpload)
{
var filePath = GetUnusedFilePath(directorypath, file, ext);
UploadFile(filePath);
}
}
}
I solved this by creating new folders with special names using the code below:
DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(FileDirectory);
FileSystemInfo[] filesAndDirs = hdDirectoryInWhichToSearch.GetFileSystemInfos("*" + FullFileName + "*");
int i = filesAndDirs.Length;
if (i>1)
{
FileName = Filename + "(" + i ")";
}
So what this does is that it will count how many files we have in that folder with the same name, so I have to check if we have more than 1 file, then change it's name to file(1).
Thank you to everyone that tried to help me, much appreciated.
I have a source and destination path with the same folder and file names (source has some extra files). my question is when I have cut source locations files and folders and to paste the destination location
how to copied initially the extra files(destination not having files)?
how to through the error after paste the extra files "the folder and files already exist do you want to replace it" message?
after getting the response how can I move and delete the source files?
somebody can help me guys I am stuck with this logic nearly 2 days.
Note: am the beginner of the C# server side code.
thanks, advance. Hi All, thank you for your reply, I have written the same structure with #RezaNoei mentioned my code was
private void DirectoryCopy(string sourceDirName, string destDirName, bool replace, string action)
{
try
{
// Gets the subdirectories for the specified directory.
var dir = new DirectoryInfo(sourceDirName);
var dirs = dir.GetDirectories();
// If the destination directory doesn't exist, creates it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Gets the files in the directory and copy them to the new location.
var files = dir.GetFiles();
foreach (var file in files)
{
var oldPath = Path.Combine(sourceDirName, file.Name);
var temppath = Path.Combine(destDirName, file.Name);
var fileExist = File.Exists(temppath);
if (!fileExist)
{
if (action != "paste")
{
file.CopyTo(temppath, true);
}
else
{
File.Move(oldPath, temppath);
}
}
else if (fileExist && replace)
{
File.Delete(temppath);
if (action != "paste")
{
file.CopyTo(temppath, true);
}
else
{
File.Move(oldPath, temppath);
}
}
}
if (action == "paste")
{
DeleteDirectory(sourceDirName);
}
}
catch (Exception e)
{
throw e;
}
}
Use this Function:
Note: If you are developing a web-application and you want to alert this through Html, it doesn't help you. Please read the Next Section "For Web-Application"
Note2: this function doesn't check for inner folders. so if you have a nested path, we should write a recursive function.
public void Copy(string Source, string Destination)
{
string[] SourceFiles = System.IO.Directory.GetFiles(Source);
for (int i = 0; i < SourceFiles.Length; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(SourceFiles[i]));
if (System.IO.File.Exists(DestinationFilePath))
{
var DialogResult = MessageBox.Show($"File `{System.IO.Path.GetFileName(SourceFiles[i])}` Exists in the Destination. Are you want to overwrite this file ?", "File Exist !", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.Yes)
System.IO.File.Copy(SourceFiles[i], DestinationFilePath, true);
}
else
{
System.IO.File.Copy(SourceFiles[i], DestinationFilePath);
}
}
}
For Web-Application:
You actually need to interact with User. And it make our work more complicated than before.
You will Work with Session and you must keep track of this Operation. I don't know which technology are you using but you must but added these ones to Session:
1-1 **Destination Folder**
1-2 **List of Duplicated SourceFiles**
So:
Next Step is to copy non-duplicated files and Added Duplication on Session:
public void Copy(string Source, string Destination)
{
/// Set Session ....
Session["Destination"] = Destination;
List<string> DuplicatedFiles = new List<string>();
string[] SourceFiles = System.IO.Directory.GetFiles(Source);
for (int i = 0; i < SourceFiles.Length; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(SourceFiles[i]));
if (System.IO.File.Exists(DestinationFilePath))
{
// Add into Duplication List
DuplicatedFiles.Add(SourceFiles[i]);
}
else
{
System.IO.File.Copy(SourceFiles[i], DestinationFilePath);
}
}
/// Set Session .....
Session["DouplicatedFiles"] = DuplicatedFiles;
}
above code is psudo and the goal is Clear.
Next Step is to show the result of copying or duplications:
I don'n know how do you want to implement such a view for duplication errors and it will not the part of the answer. anyway you may want to let the user to choose the action on each files separately or whole of them at the same time.
depend on your preferences you will have an ActionMethod (In MVC) or something else in WebForm that will do these things:
(If user doesn't want replace the files, it's easy to forget the action)
public void CopyDuplications(bool Overwrite)
{
if (!Overwrite)
return "OK";
else
{
string Destination = Session["Destination"] as string;
var DuplicatedFiles = Session["DouplicatedFiles"] as List<string>();
for (int i = 0; i< DuplicatedFiles.Count; i++)
{
string DestinationFilePath = System.IO.Path.Combine(Destination, System.IO.Path.GetFileName(DuplicatedFiles[i]));
System.IO.File.Copy(DuplicatedFiles[i], DestinationFilePath, true);
}
}
}
We have a process from third-party vendor to drop sales and invetory data everyday and could have any of the following scenarios
Drop the right file. (Naming standard: test.xls)
Drop the right file but not follow the right naming standard. (Other
names could be test_mmddyyyy or testmmddyyyy)
No file dropped.
I am trying to build my logic around these scenarios and stuck at how to build my logic when the file exists but does not have the right naming standard and check for this condition and change the name of the file to the appropriate naming standard.
public void Main()
{
try
{
string filefullpathname = #"C:\Temp\test.xls";
if (File.Exists(filefullpathname) == false)
{
Console.WriteLine("File does not exist in the path");
}
// file exists but right naming standard not followed (Other names could be test_mmddyyyy or testmmddyyyy)
// how to check for this condition and change the name of the file to the naming standard
else
{
string dirname = #"C:\Temp\";
DirectoryInfo directory = new DirectoryInfo(dirname);
string filepartialname = "test";
FileInfo[] fileindirectory = directory.GetFiles(filepartialname + "*");
foreach (FileInfo filename in fileindirectory)
{
string fullname = filename.FullName;
bool ind = Path.HasExtension(fullname);
if (ind == false)
{
File.Move(fullname, directory + filepartialname + ".xls");
}
else
{
File.Move(fullname, directory + filepartialname + ".xls");
}
}
}
Dts.TaskResult = (int)ScriptResults.Success;
}
catch (Exception error)
{
Console.WriteLine(error);
}
}
It is not really clear as to if it is only the file name or a missing extension. So I put in both.
public void Main()
{
try
{
string dirname = #"C:\Temp\";
DirectoryInfo directory = new DirectoryInfo(dirname);
string filepartialname = "test";
FileInfo[] fileindirectory = directory.GetFiles(filepartialname + "*");
foreach (FileInfo filename in fileindirectory)
{
if (filename.Extension == "")
{
//doesn't have an extension
}
else if (!Regex.IsMatch(filename.Name.Replace(filename.Extension, ""), #"^[A-Z|a-z]$"))
{
//contains more then just test
}
else
{
//file is good
}
}
}
catch (Exception error)
{
Console.WriteLine(error);
}
}
Your explanation of what your inputs could be, and how you want to move those inputs isn't super clear, but this should get you started:
var expectedFilename = Path.Combine(someOtherDirectory, "test.xls");
// Matches test* and *.xls
var relevantFiles = Directory
.EnumerateFiles(searchDirectory, "*", SearchOption.TopDirectoryOnly)
.Where(f => Path.GetFileName(f).StartsWith("test") || Path.GetExtension(f).Equals(".xls"))
foreach (var file in relevantFiles)
{
// If there's more than one file matching the pattern, last one in wins
File.Move(file, expectedFilename);
}
I have the following code, which scans a directory and puts files containing "a" within its filename to a new folder A. Similarly, it puts files with "b" within its filename to a new folder called B. Since the if statements are basically the same, with the only thing that changes being the letter "a" or "b" and being sent to either destA or destb (desitinations), how can I trim this code down? I know there is a better way because much of the code is repeated... Thanks.
static void Main()
{
string path = #"C:\Users\me\Desktop\FOLDER";
string destA = #"C:\Users\me\Desktop\FOLDER\A";
string destB = #"C:\Users\me\Desktop\FOLDER\B";
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] filesxx = dir.GetFiles();
foreach (FileInfo filexx in filesxx)
{
if (filexx.Name.Contains("a"))
{
if (!Directory.Exists(destA))
Directory.CreateDirectory(destA);
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destA, filexx.Name), true);
}
else if (filexx.Name.Contains("b"))
{
if (!Directory.Exists(destB))
Directory.CreateDirectory(destB);
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destB, filexx.Name), true);
}
else
{
Console.WriteLine("Other: ", filexx);
}
}
Console.Read();
}
Create a method like:
private Boolean MoveFile(FileInfo filexx, String nameMatch, String destDirectory) {
Boolean result = false;
if (filexx.Name.Contains(nameMatch)) {
if (!Directory.Exists(destDirectory)) {
Directory.CreateDirectory(destDirectory);
}
Console.WriteLine(filexx);
filexx.CopyTo(Path.Combine(destDirecotry, filexx.Name), true);
result = true;
}
return result;
}
Then just call it as necessary.
foreach(FileInfo filexx in filesxx) {
if (!MoveFile(filexx, "a", destA)) {
if (!MoveFile(filexx, "b", destB)) {
Console.WriteLine("Other: ", filexx);
}
}
}
Of course, there is a potential precedence issue here. What if the file is named "abcd"? Should it go to the A folder or the B folder?
If all you're looking for is less code, this should do it.
public static void Main()
{
const string TargetPath = #"C:\Users\me\Desktop\FOLDER";
var dir = new DirectoryInfo(TargetPath);
var files = dir.GetFiles();
foreach (var file in files.Where(file => !CopyFile(TargetPath, file, "a")).Where(file => !CopyFile(TargetPath, file, "b")))
{
Console.WriteLine("Other: " + file.Name);
}
Console.Read();
}
private static bool CopyFile(string dir, FileInfo file, string match)
{
if (!file.Name.Contains(match))
{
return false;
}
dir = dir + "\\" + match.ToUpper();
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
Console.WriteLine(file);
file.CopyTo(Path.Combine(dir, file.Name), true);
return true;
}