Related
I would like to create a method which takes either a filename as a string or a FileInfo and adds an incremented number to the filename if the file exists. But can't quite wrap my head around how to do this in a good way.
For example, if I have this FileInfo
var file = new FileInfo(#"C:\file.ext");
I would like the method to give me a new FileInfo with C:\file 1.ext if C:\file.ext
existed, and C:\file 2.ext if C:\file 1.ext existed and so on. Something like this:
public FileInfo MakeUnique(FileInfo fileInfo)
{
if(fileInfo == null)
throw new ArgumentNullException("fileInfo");
if(!fileInfo.Exists)
return fileInfo;
// Somehow construct new filename from the one we have, test it,
// then do it again if necessary.
}
public FileInfo MakeUnique(string path)
{
string dir = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
for (int i = 1; ;++i) {
if (!File.Exists(path))
return new FileInfo(path);
path = Path.Combine(dir, fileName + " " + i + fileExt);
}
}
Obviously, this is vulnerable to race conditions as noted in other answers.
Lots of good advice here. I ended up using a method written by Marc in an answer to a different question. Reformatted it a tiny bit and added another method to make it a bit easier to use "from the outside". Here is the result:
private static string numberPattern = " ({0})";
public static string NextAvailableFilename(string path)
{
// Short-cut if already available
if (!File.Exists(path))
return path;
// If path has extension then insert the number pattern just before the extension and return next filename
if (Path.HasExtension(path))
return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern));
// Otherwise just append the pattern to the path and return next filename
return GetNextFilename(path + numberPattern);
}
private static string GetNextFilename(string pattern)
{
string tmp = string.Format(pattern, 1);
if (tmp == pattern)
throw new ArgumentException("The pattern must include an index place-holder", "pattern");
if (!File.Exists(tmp))
return tmp; // short-circuit if no matches
int min = 1, max = 2; // min is inclusive, max is exclusive/untested
while (File.Exists(string.Format(pattern, max)))
{
min = max;
max *= 2;
}
while (max != min + 1)
{
int pivot = (max + min) / 2;
if (File.Exists(string.Format(pattern, pivot)))
min = pivot;
else
max = pivot;
}
return string.Format(pattern, max);
}
Only partially tested it so far, but will update if I find any bugs with it. (Marcs code works nicely!) If you find any problems with it, please comment or edit or something :)
Not pretty, but I've had this for a while :
private string getNextFileName(string fileName)
{
string extension = Path.GetExtension(fileName);
int i = 0;
while (File.Exists(fileName))
{
if (i == 0)
fileName = fileName.Replace(extension, "(" + ++i + ")" + extension);
else
fileName = fileName.Replace("(" + i + ")" + extension, "(" + ++i + ")" + extension);
}
return fileName;
}
Assuming the files already exist:
File.txt
File(1).txt
File(2).txt
the call getNextFileName("File.txt") will return "File(3).txt".
Not the most efficient because it doesn't use binary search, but should be ok for small file count. And it doesn't take race condition into account...
If checking if the file exists is too hard you can always just add a date and time to the file name to make it unique:
FileName.YYYYMMDD.HHMMSS
Maybe even add milliseconds if necessary.
If the format doesn't bother you then you can call:
try{
string tempFile=System.IO.Path.GetTempFileName();
string file=System.IO.Path.GetFileName(tempFile);
//use file
System.IO.File.Delete(tempFile);
}catch(IOException ioe){
//handle
}catch(FileIOPermission fp){
//handle
}
PS:- Please read more about this at msdn before using.
/// <summary>
/// Create a unique filename for the given filename
/// </summary>
/// <param name="filename">A full filename, e.g., C:\temp\myfile.tmp</param>
/// <returns>A filename like C:\temp\myfile633822247336197902.tmp</returns>
public string GetUniqueFilename(string filename)
{
string basename = Path.Combine(Path.GetDirectoryName(filename),
Path.GetFileNameWithoutExtension(filename));
string uniquefilename = string.Format("{0}{1}{2}",
basename,
DateTime.Now.Ticks,
Path.GetExtension(filename));
// Thread.Sleep(1); // To really prevent collisions, but usually not needed
return uniquefilename;
}
As DateTime.Ticks has a resolution of 100 nanoseconds, collisions are extremely unlikely. However, a Thread.Sleep(1) will ensure that, but I doubt that it's needed
Insert a new GUID into the file name.
I must throw my 2-cents in. This is how I did it and it works for my use.
private static string IterateFileName(string fileName)
{
if (!File.Exists(fileName)) return fileName;
FileInfo fi = new FileInfo(fileName);
string ext = fi.Extension;
string name = fi.FullName.Substring(0, fi.FullName.Length - ext.Length);
int i = 2;
while (File.Exists($"{name}_{i}{ext}"))
{
i++;
}
return $"{name}_{i}{ext}";
}
The idea is to get a list of the existing files, parse out the numbers, then make the next highest one.
Note: This is vulnerable to race conditions, so if you have more than one thread creating these files, be careful.
Note 2: This is untested.
public static FileInfo GetNextUniqueFile(string path)
{
//if the given file doesn't exist, we're done
if(!File.Exists(path))
return new FileInfo(path);
//split the path into parts
string dirName = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
//get the directory
DirectoryInfo dir = new DirectoryInfo(dir);
//get the list of existing files for this name and extension
var existingFiles = dir.GetFiles(Path.ChangeExtension(fileName + " *", fileExt);
//get the number strings from the existing files
var NumberStrings = from file in existingFiles
select Path.GetFileNameWithoutExtension(file.Name)
.Remove(0, fileName.Length /*we remove the space too*/);
//find the highest existing number
int highestNumber = 0;
foreach(var numberString in NumberStrings)
{
int tempNum;
if(Int32.TryParse(numberString, out tempnum) && tempNum > highestNumber)
highestNumber = tempNum;
}
//make the new FileInfo object
string newFileName = fileName + " " + (highestNumber + 1).ToString();
newFileName = Path.ChangeExtension(fileName, fileExt);
return new FileInfo(Path.Combine(dirName, newFileName));
}
Instead of poking the disk a number of times to find out if it has a particular variant of the desired file name, you could ask for the list of files that already exist and find the first gap according to your algorithm.
public static class FileInfoExtensions
{
public static FileInfo MakeUnique(this FileInfo fileInfo)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName);
return new FileInfo(newfileName);
}
}
public class FileUtilities
{
public string GetNextFileName(string fullFileName)
{
if (fullFileName == null)
{
throw new ArgumentNullException("fullFileName");
}
if (!File.Exists(fullFileName))
{
return fullFileName;
}
string baseFileName = Path.GetFileNameWithoutExtension(fullFileName);
string ext = Path.GetExtension(fullFileName);
string filePath = Path.GetDirectoryName(fullFileName);
var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext)
.Select(x => Path.GetFileNameWithoutExtension(x).Substring(baseFileName.Length))
.Select(x =>
{
int result;
return Int32.TryParse(x, out result) ? result : 0;
})
.Distinct()
.OrderBy(x => x)
.ToList();
var firstGap = numbersUsed
.Select((x, i) => new { Index = i, Item = x })
.FirstOrDefault(x => x.Index != x.Item);
int numberToUse = firstGap != null ? firstGap.Item : numbersUsed.Count;
return Path.Combine(filePath, baseFileName) + numberToUse + ext;
}
}
Here's one that decouples the numbered naming question from the check of the filesystem:
/// <summary>
/// Finds the next unused unique (numbered) filename.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="inUse">Function that will determine if the name is already in use</param>
/// <returns>The original filename if it wasn't already used, or the filename with " (n)"
/// added to the name if the original filename is already in use.</returns>
private static string NextUniqueFilename(string fileName, Func<string, bool> inUse)
{
if (!inUse(fileName))
{
// this filename has not been seen before, return it unmodified
return fileName;
}
// this filename is already in use, add " (n)" to the end
var name = Path.GetFileNameWithoutExtension(fileName);
var extension = Path.GetExtension(fileName);
if (name == null)
{
throw new Exception("File name without extension returned null.");
}
const int max = 9999;
for (var i = 1; i < max; i++)
{
var nextUniqueFilename = string.Format("{0} ({1}){2}", name, i, extension);
if (!inUse(nextUniqueFilename))
{
return nextUniqueFilename;
}
}
throw new Exception(string.Format("Too many files by this name. Limit: {0}", max));
}
And here's how you might call it if you are using the filesystem
var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));
private async Task<CloudBlockBlob> CreateBlockBlob(CloudBlobContainer container, string blobNameToCreate)
{
var blockBlob = container.GetBlockBlobReference(blobNameToCreate);
var i = 1;
while (await blockBlob.ExistsAsync())
{
var newBlobNameToCreate = CreateRandomFileName(blobNameToCreate,i.ToString());
blockBlob = container.GetBlockBlobReference(newBlobNameToCreate);
i++;
}
return blockBlob;
}
private string CreateRandomFileName(string fileNameWithExtension, string prefix=null)
{
int fileExtPos = fileNameWithExtension.LastIndexOf(".", StringComparison.Ordinal);
if (fileExtPos >= 0)
{
var ext = fileNameWithExtension.Substring(fileExtPos, fileNameWithExtension.Length - fileExtPos);
var fileName = fileNameWithExtension.Substring(0, fileExtPos);
return String.Format("{0}_{1}{2}", fileName, String.IsNullOrWhiteSpace(prefix) ? new Random().Next(int.MinValue, int.MaxValue).ToString():prefix,ext);
}
//This means there is no Extension for the file and its fine attaching random number at the end.
return String.Format("{0}_{1}", fileNameWithExtension, new Random().Next(int.MinValue, int.MaxValue));
}
I use this code to create a consecutive _1,_2,_3 etc.. file name everytime a file exists in the blob storage.
Hope this self iterating function may help. It works fine for me.
public string getUniqueFileName(int i, string filepath, string filename)
{
string path = Path.Combine(filepath, filename);
if (System.IO.File.Exists(path))
{
string name = Path.GetFileNameWithoutExtension(filename);
string ext = Path.GetExtension(filename);
i++;
filename = getUniqueFileName(i, filepath, name + "_" + i + ext);
}
return filename;
}
This is an answer to question in this Link, but they marked it as a duplicate, so I post my answer here.
I created this proof of concept class (may contain bugs).
More explanation in code comments.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApp
{
class Program
{
static void Main( string[] args )
{
var testFilePaths = new List<string>
{
#"c:\test\file.txt",
#"c:\test\file(1).txt",
#"c:\test\file(2).txt",
#"c:\TEST2\file(3).txt",
#"c:\test\file(5).txt",
#"c:\test\file(5)abc.txt",
#"c:\test\file(5).avi"
};
// inspect in debbuger for correct values
var withSuffix = new DecomposedFilePath( "c:\\files\\file(13).txt");
var withoutSuffix = new DecomposedFilePath( "c:\\files\\file(abc).txt");
var withExtraNumber = new DecomposedFilePath( "c:\\files\\file(34)xyz(35).txt"); // "(34)" in the middle should be ignored
DecomposedFilePath changedSuffix = withExtraNumber.ReplaceSuffix( 1999 ); // "file(34)xyz(35).txt" -> "file(34)xyz(1999).txt"
DecomposedFilePath removedSuffix = changedSuffix.ReplaceSuffix( null ); // "file(34)xyz(1999).txt" -> "file(34)xyz.txt"
var testPath = new DecomposedFilePath( "c:\\test\\file.txt");
DecomposedFilePath nextPath1 = testPath.GetFirstFreeFilePath( testFilePaths );
// update our list
testFilePaths.Add( nextPath1.FullFilePath );
DecomposedFilePath nextPath2 = testPath.GetFirstFreeFilePath( testFilePaths );
testFilePaths.Add( nextPath2.FullFilePath );
DecomposedFilePath nextPath3 = testPath.GetFirstFreeFilePath( testFilePaths );
}
}
public sealed class DecomposedFilePath
{
public DecomposedFilePath( string filePath )
{
FullFilePath = Path.GetFullPath( filePath );
}
// "c:\myfiles\file(4).txt"
public string FullFilePath { get; }
// "file" or "file(1)"
public string FileNameWithoutExt => Path.GetFileNameWithoutExtension( FullFilePath );
// "file(13)" -> "file"
public string FileNameWithoutExtAndSuffix => FileNameWithoutExt.Substring( 0, FileNameWithoutExt.Length - Suffix.Length ); // removes suffix
// ".txt"
public string Extenstion => Path.GetExtension( FullFilePath );
// "c:\myfiles"
public string DirectoryPath => Path.GetDirectoryName( FullFilePath );
// "file(23)" -> "23", file -> stirng.Empty
public string Suffix
{
get
{
// we want to extract suffix from file name, e.g. "(34)" from "file(34)"
// I am not good at regex, but I hope it will work correctly
var regex = new Regex( #"\([0-9]+\)$" );
Match match = regex.Match( FileNameWithoutExt );
if (!match.Success) return string.Empty; // suffix not found
return match.Value; // return "(number)"
}
}
// tranlates suffix "(33)" to 33. If suffix is does not exist (string.empty), returns null (int?)
public int? SuffixAsInt
{
get
{
if (Suffix == string.Empty) return null;
string numberOnly = Suffix.Substring( 1, Suffix.Length - 2 ); // remove '(' from beginning and ')' from end
return int.Parse( numberOnly );
}
}
// e.g. input is suffix: 56 then it changes file name from "file(34)" to "file(56)"
public DecomposedFilePath ReplaceSuffix( int? suffix ) // null - removes suffix
{
string strSuffix = suffix is null ? string.Empty : $"({suffix})"; // add ( and )
string path = Path.Combine( DirectoryPath, FileNameWithoutExtAndSuffix + strSuffix + Extenstion ); // build full path
return new DecomposedFilePath( path );
}
public DecomposedFilePath GetFirstFreeFilePath( IEnumerable<string> filesInDir )
{
var decomposed = filesInDir
// convert all paths to our class
.Select( x => new DecomposedFilePath( x ) )
// pick files only with the same extensionm as our base file, ignore case
.Where( x => string.Equals( Extenstion, x.Extenstion, StringComparison.OrdinalIgnoreCase) )
// pick files only with the same name (ignoring suffix)
.Where( x => string.Equals( FileNameWithoutExtAndSuffix, x.FileNameWithoutExtAndSuffix, StringComparison.OrdinalIgnoreCase) )
// with the same directory
.Where( x => string.Equals( DirectoryPath, x.DirectoryPath, StringComparison.OrdinalIgnoreCase) )
.ToList(); // create copy for easier debugging
if (decomposed.Count == 0) return this; // no name collision
int? firstFreeSuffix = Enumerable.Range( 1, int.MaxValue) // start numbering duplicates from 1
.Select( x => (int?) x) // change to int? because SuffixAsInt is of that type
.Except( decomposed.Select( x => x.SuffixAsInt) ) // remove existing suffixes
.First(); // get first free suffix
return ReplaceSuffix( firstFreeSuffix );
}
public override string ToString() => FullFilePath;
}
}
This is just a string operation; find the location in the filename string where you want to insert the number, and re-construct a new string with the number inserted. To make it re-usable, you might want to look for a number in that location, and parse it out into an integer, so you can increment it.
Please note that this in general this way of generating a unique filename is insecure; there are obvious race condition hazards.
There might be ready-made solutions for this in the platform, I'm not up to speed with C# so I can't help there.
Take a look at the methods in the Path class, specifically Path.GetFileNameWithoutExtension(), and Path.GetExtension().
You may even find Path.GetRandomFileName() useful!
Edit:
In the past, I've used the technique of attempting to write the file (with my desired name), and then using the above functions to create a new name if an appropriate IOException is thrown, repeating until successful.
This method will add a index to existing file if needed:
If the file exist, find the position of the last underscore. If the content after the underscore is a number, increase this number. otherwise add first index. repeat until unused file name found.
static public string AddIndexToFileNameIfNeeded(string sFileNameWithPath)
{
string sFileNameWithIndex = sFileNameWithPath;
while (File.Exists(sFileNameWithIndex)) // run in while scoop so if after adding an index the the file name the new file name exist, run again until find a unused file name
{ // File exist, need to add index
string sFilePath = Path.GetDirectoryName(sFileNameWithIndex);
string sFileName = Path.GetFileNameWithoutExtension(sFileNameWithIndex);
string sFileExtension = Path.GetExtension(sFileNameWithIndex);
if (sFileName.Contains('_'))
{ // Need to increase the existing index by one or add first index
int iIndexOfUnderscore = sFileName.LastIndexOf('_');
string sContentAfterUnderscore = sFileName.Substring(iIndexOfUnderscore + 1);
// check if content after last underscore is a number, if so increase index by one, if not add the number _01
int iCurrentIndex;
bool bIsContentAfterLastUnderscoreIsNumber = int.TryParse(sContentAfterUnderscore, out iCurrentIndex);
if (bIsContentAfterLastUnderscoreIsNumber)
{
iCurrentIndex++;
string sContentBeforUnderscore = sFileName.Substring(0, iIndexOfUnderscore);
sFileName = sContentBeforUnderscore + "_" + iCurrentIndex.ToString("000");
sFileNameWithIndex = sFilePath + "\\" + sFileName + sFileExtension;
}
else
{
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
else
{ // No underscore in file name. Simple add first index
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
return sFileNameWithIndex;
}
I did it like this:
for (int i = 0; i <= 500; i++) //I suppose the number of files will not pass 500
{ //Checks if C:\log\log+TheNumberOfTheFile+.txt exists...
if (System.IO.File.Exists(#"C:\log\log"+conta_logs+".txt"))
{
conta_logs++;//If exists, then increment the counter
}
else
{ //If not, then the file is created
var file = System.IO.File.Create(#"C:\log\log" + conta_logs + ".txt");
break; //When the file is created we LEAVE the *for* loop
}
}
I think this version is not so hard like the others, and It's a straightforward answer for what the user wanted.
If you need just a unique file name, so, how about this?
Path.GetRandomFileName()
I ran into this problem and, since none of the other answers seemed to have solved it in the way I wanted to, I did it on my own.
static string CheckIfFileExists(string filePath)
{
if (File.Exists(filePath))
{
string parentDir = Directory.GetParent(filePath).FullName;
string fileName = new DirectoryInfo(filePath).Name;
string extension = Path.GetExtension(fileName);
fileName = Path.GetFileNameWithoutExtension(fileName);
if (CheckIfFileNameHasIndex(fileName))
{
string strIndex = fileName[(fileName.LastIndexOf('(')+1)..fileName.LastIndexOf(')')]; //range
int index = int.Parse(strIndex);
index++;
fileName = fileName.Substring(0, fileName.LastIndexOf('(')) + "(" + index + ')';
filePath = Path.Combine(parentDir, fileName + extension);
return CheckIfFileExists(filePath);
}
else
{
fileName = fileName + " (1)";
filePath = Path.Combine(parentDir, fileName + extension);
return CheckIfFileExists(filePath);
}
}
return filePath;
}
//checks if filename has an index (e.g. "file(2).jpg")
static bool CheckIfFileNameHasIndex(string fileName)
{
bool isSuccessful = false;
if (fileName.LastIndexOf('(')!=-1 && fileName.LastIndexOf(')')!=-1)
{
string index = fileName[(fileName.LastIndexOf('(')+1)..fileName.LastIndexOf(')')]; //range
int result;
isSuccessful = int.TryParse(index, out result);
}
return isSuccessful;
}
The method CheckIfFileExists is recursive, so in theory it should be able to handle a potentially unlimited number of duplicates (e.g. "file (3484939).txt"). Of course, in reality, what happens is that the maximum imposed filename length of your operating system and stuff like eventually become a bottleneck.
I have written a method that returns "next" file name with number.
Supports numbering from 1 to 99.
Examples:
C:\Recovery.txt → C:\Recovery1.txt
C:\Recovery1.txt → C:\Recovery2.txt
How to call:
while (File.Exists( path ))
path = NextFileNum( path );
internal static string NextFileNum( string path )
{
string filename = Path.GetFileNameWithoutExtension( path );
string ext = Path.GetExtension( path );
string dir = Path.GetDirectoryName( path );
for (int i = 99; i > 0; i--)
{
if (filename.EndsWith( i.ToString() ))
{
string suffix = ( i + 1 ).ToString();
filename = filename.Substring( 0, filename.Length - suffix.Length ) + suffix;
return Path.Combine( dir, filename + ext );
}
}
filename = filename + "1";
return Path.Combine( dir, filename + ext );
}
public static string MakeUniqueFilePath(string filePath)
{
if (!File.Exists(filePath)) return filePath;
var directory = Path.GetDirectoryName(filePath);
var fileName = Path.GetFileNameWithoutExtension(filePath);
var fileExt = Path.GetExtension(filePath);
var i = 1;
do
{
filePath = Path.Combine(directory, fileName + "(" + i + ")" + fileExt);
i++;
} while (File.Exists(filePath));
return filePath;
}
Returns files like so:
test.txt
test(1).txt
test(2).txt
etc.
Notes:
Can handle filenames without extensions
Can Handle directories included in the file path.
Does not handle file creation race conditions when saving.
This question already has answers here:
FileUpload - if file name exist concat a number between parentheses at the end of name
(3 answers)
Closed 5 years ago.
I have a fileupload control, What I want to achieve is to concat a nuber to it, in order it to be unique.
All the posts that I found are talking about adding a GUID or DateTime
What i want to achieve is if the file exist in folder then the file name will be filename + (counter)
Example:
The folder contain a file name- file.png
1) When I upload the same file name again, the existing one wont get delete and the new one will be called file(1).png.
2) When I upload the same file name again, (file.png)
now the new file will be called file(2)
I have this code which handle the 1'st case but not the second:
public static string GetUniqueName(string fileName)
{
string dir = Globals.Directories.GetCustomCategoryThumbnailDir();
string fileExtension = Path.GetExtension(fileName);
string fileNameWE = Path.GetFileNameWithoutExtension(fileName);
string[] files = Directory.GetFiles(dir, "*" + fileExtension)
.Select(Path.GetFileName)
.ToArray();
string uniqueName = fileNameWE;
int nextNum = 0;
bool fileExist = false;
string pattern = #"(.*)\(([\d]+)\)";
foreach (var file in files)
{
var tempFileName = Path.GetFileNameWithoutExtension(file);
var match = Regex.Match(tempFileName, pattern);
if (tempFileName.Equals(fileNameWE))
{
// file exist in folder
fileExist = true;
}
if (tempFileName.StartsWith(fileNameWE) && match.Success)
{
// there is a file name that start with "fileToUpload" name, we want to to take the number
nextNum = Convert.ToInt32(match.Groups[2].Value);
nextNum++;
}
}
if (nextNum == 0 && !fileExist)
{
// filename dont exist
return fileNameWE + fileExtension;
}
if (nextNum == 0 && fileExist)
{
// the file name exist without (1)
fileNameWE = $"{fileNameWE}(1)";
return fileNameWE + fileExtension;
}
else
{
var haveParentessis = Regex.Match(fileNameWE, pattern);
if (haveParentessis.Success)
{
// we need to reset the nextNum
nextNum = 1;
}
// return the new unique name with suffix
fileNameWE = string.Format("{0}({1})", fileNameWE, nextNum);
return fileNameWE + fileExtension;
}
}
How can I achieve that?
Yeah, that's cause your while loop as pointed below is local to the function and thus the counter variable which is also a local variable won't be maintained it's current value. So in essence every request you get a new page instance and thus your counter will get re-initialized to 0
while (File.Exists(Path.Combine(dir, fileNameWE + fileExtension)))
{
fileNameWE = fileNameWE + "(" + counter.ToString() + ")";
counter++;
}
To get this done, you should store the counter variable in Session and retrieve it back. Don't forget it's an web application and thus the state won't be maintained unless you do it explicitly
if (File.Exists(Path.Combine(dir, fileNameWE + fileExtension)))
{
counter = (Session["counter"] != null) ? ((int)Session["counter"])++ : default(int);
fileNameWE = fileNameWE + "(" + counter.ToString() + ")";
Session["counter"] = counter;
}
I got this to work by using regular expressions, below. No persistent counter is required. I think the concat problem is because there's nothing done to deal with the existing (1) suffix. I coded this potential solution and hope it helps! NB: only lightly tested in debugger, and I am a novice
{
string fileName = #"C:\Uploads\Testfile.bin";
string fileExtension = Path.GetExtension(fileName);
string fileNameWE = Path.GetFileNameWithoutExtension(fileName);
string pattern = #"(.*)\(([\d]+)\)$";
var match = Regex.Match(fileNameWE, pattern);
if (match.Success)
{
int nextNum = Convert.ToInt32(match.Groups[2].Value);
nextNum++;
fileNameWE = $"{match.Groups[1].Value}({nextNum})";
}
else
{
fileNameWE = $"{fileNameWE}(1)";
}
}
I have a piece of code which works nicely. However I need to close the file so I can perform file.move() function, this doesn't work because the file is used by another process. I need to use the correct file handle - can you guide me in the right direction?
static void DSCheckForDuplicates(string incomingfolder, string incomingarchivefolder, string quarantinefolder)
{
string[] F1 = Directory.GetFiles(incomingfolder);
string fname = "";
long FileOne;
long FileTwo;
bool FilesAreTrullyIdentical;
string FileStatusValue = "";
string Result = "";
string NewLocation = "";
foreach (string fileName in F1)
{
// FILE EXCLUSION LIST FROM DUPLICATE FILE CHECKS
if (fileName.Contains("xxx.DAT") || fileName.Contains("xxx.txt") || fileName.Contains("OrderHead.txt") )
{
Console.WriteLine("\nKnown file type..");
}
else
{
fname = Path.GetFileName(fileName);
FilesAreTrullyIdentical = false;
Console.WriteLine("Files present : The file is {0}...Press any key\n", fileName);
//Console.ReadKey();
if (File.Exists(incomingarchivefolder + #"\" + fname))
{
DuplicateFlag = true;
FileStatusValue = "DuplicateFilename";
DuplicateFileCounter++;
Narative += string.Format("\n________________________________________________________________________________________________________________\nFile Exception :{0}\n####################\n", DuplicateFileCounter );
Narative += string.Format ("Same filename exists in the two compared directories, Checking potential duplicate file contents in :{0}................\n", fileName);
FileInfo f1 = new FileInfo(fileName);
FileOne = f1.Length;
FileInfo f2 = new FileInfo(incomingarchivefolder + #"\" + fname);
FileTwo = f2.Length;
//if (FileOne == FileTwo)
//{
byte[] firstHash = MD5.Create().ComputeHash(f1.OpenRead());
byte[] secondHash = MD5.Create().ComputeHash(f2.OpenRead());
for (int i = 0; i < firstHash.Length; i++)
{
FilesAreTrullyIdentical = true;
if (firstHash[i] != secondHash[i])
FilesAreTrullyIdentical = false;
}
if (FilesAreTrullyIdentical == true)
{
FileStatusValue = "DuplicationFileNameDuplicateContents";
Console.WriteLine("Processed : WARNING!!! identical FILES contents FOUND {0}\n and {1}\n..............\n", fileName, incomingarchivefolder + #"\" + fname);
Narative += string.Format("\tProcessed : Please delete from incoming, WARNING!!! identical FILES contents\n\nPLEASE DELETE FILE:\t{0}..............\n", fileName);
Result = Path.GetFileName(fileName);
NewLocation += quarantinefolder + "\\" + Result;
Console.WriteLine("\n\n {0} ->\nMoving to {1} , press any key", fileName, NewLocation);
Console.ReadKey();
//File.Move(fileName, NewLocation); // THIS DOESNT WORK
You could capture the stream from f1.OpenRead() into a variable & pass that calling Close() when your done, instead however you should put the stream and MD5 reference within a using construct as currently you leave them undisposed. (This will also close the stream for you)
byte[] firstHash;
using (var stream = f1.OpenRead())
using (var md5 = MD5.Create())
{
firstHash = md5.ComputeHash(stream);
}
I am trying to make a web server in C#, i need to get the requested url and then list the files and folders requested.This is good to get the first directory .
For Eg. My webserver root is c:\test when i open localhost i get the contents of test folder. Say Data is subfolder of c:\test, i can click on data from the browser and get into C:\test\data now when i click on any folder then the get request comes with %2F instead of c:\test\data\ok and so i am stuck.
Code to Recieve The Request :
sRequest = sBuffer.Substring(0, iStartPos - 1);
sRequest.Replace("\\", "/");
if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/")))
{
sRequest = sRequest + "/";
}
iStartPos = sRequest.LastIndexOf("/") + 1;
sRequestedFile = sRequest.Substring(iStartPos);
sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/") - 3);
if (sDirName == "/")
sLocalDir = sMyWebServerRoot;
else
{
//Get the Virtual Directory
// sLocalDir = GetLocalPath(sMyWebServerRoot, sDirName);
Console.WriteLine("i am here");
sDirName = sDirName.Substring(1, sDirName.Length - 2);
//sDirName = sDirName.Replace("/", "\\");
Console.WriteLine("Amit:" + sDirName);
string test1 = Path.Combine("C:\\test\\", sDirName);
sLocalDir = Path.Combine(#"C:\\test", sDirName);
}
Now to List Dir I have the following Function :
public String listdir(string sLocaldir,string sDirName)
{
string sresult = "";
StringBuilder sb = new StringBuilder();
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine("<title>Test</title>");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine("<h1><center><u>Listing Folders Under " + sLocaldir + "</h1></u></center>");
string[] folderpaths = Directory.GetDirectories(sLocaldir);
sb.AppendLine("<font color=red><font size=5>Listing Directories<br>");
for (int i = 0; i < folderpaths.Length; i++)
{
string fpath = folderpaths[i];
string[] foldernames = fpath.Split('\\');
int j = foldernames.Length - 1;
string fname = foldernames[j];
string fullname;
if (sDirName != "/")
{
//fname= fname + "\\";
fullname = sDirName +"/"+ fname;
//fullname = fullname.Replace("\\", "/");
//fullname = Path.GetPathRoot("C:\\test");
Console.WriteLine("Get full path:" + fullname);
}
else
{
fullname = fname;
}
Console.WriteLine("Full Test:" + fullname);
//sb.AppendLine(string.Format("<img src=file.png height=20 width=20>{1}<br>",
sb.AppendLine(string.Format("<img src=file.png height=20 width=20>{1}<br>",
HttpUtility.HtmlEncode(HttpUtility.UrlEncode(fullname )),
HttpUtility.HtmlEncode(fname)));
}
string[] filePaths = Directory.GetFiles(#"C:\test");
sb.AppendLine("<font color=red><font size=5>Listing Files<br>");
for (int i = 0; i < filePaths.Length; ++i)
{
string name = Path.GetFileName(filePaths[i]);
sb.AppendLine(string.Format("<img src=file.png height=20 width=20>{1}<br>",
HttpUtility.HtmlEncode(HttpUtility.UrlEncode(name)),
HttpUtility.HtmlEncode(name)));
}
sb.AppendLine("</ul>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
sresult = sb.ToString();
return sresult;
//Console.WriteLine(sresult);
}
Any help would be highly appreciated.
Thank you
%2F is safe encoding for the / symbol. You are HTMLEncoding the / symbol in your code above.
Your approach can be much simpler see:
http://www.codeproject.com/KB/IP/mywebserver.aspx
I would like to create a method which takes either a filename as a string or a FileInfo and adds an incremented number to the filename if the file exists. But can't quite wrap my head around how to do this in a good way.
For example, if I have this FileInfo
var file = new FileInfo(#"C:\file.ext");
I would like the method to give me a new FileInfo with C:\file 1.ext if C:\file.ext
existed, and C:\file 2.ext if C:\file 1.ext existed and so on. Something like this:
public FileInfo MakeUnique(FileInfo fileInfo)
{
if(fileInfo == null)
throw new ArgumentNullException("fileInfo");
if(!fileInfo.Exists)
return fileInfo;
// Somehow construct new filename from the one we have, test it,
// then do it again if necessary.
}
public FileInfo MakeUnique(string path)
{
string dir = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
for (int i = 1; ;++i) {
if (!File.Exists(path))
return new FileInfo(path);
path = Path.Combine(dir, fileName + " " + i + fileExt);
}
}
Obviously, this is vulnerable to race conditions as noted in other answers.
Lots of good advice here. I ended up using a method written by Marc in an answer to a different question. Reformatted it a tiny bit and added another method to make it a bit easier to use "from the outside". Here is the result:
private static string numberPattern = " ({0})";
public static string NextAvailableFilename(string path)
{
// Short-cut if already available
if (!File.Exists(path))
return path;
// If path has extension then insert the number pattern just before the extension and return next filename
if (Path.HasExtension(path))
return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern));
// Otherwise just append the pattern to the path and return next filename
return GetNextFilename(path + numberPattern);
}
private static string GetNextFilename(string pattern)
{
string tmp = string.Format(pattern, 1);
if (tmp == pattern)
throw new ArgumentException("The pattern must include an index place-holder", "pattern");
if (!File.Exists(tmp))
return tmp; // short-circuit if no matches
int min = 1, max = 2; // min is inclusive, max is exclusive/untested
while (File.Exists(string.Format(pattern, max)))
{
min = max;
max *= 2;
}
while (max != min + 1)
{
int pivot = (max + min) / 2;
if (File.Exists(string.Format(pattern, pivot)))
min = pivot;
else
max = pivot;
}
return string.Format(pattern, max);
}
Only partially tested it so far, but will update if I find any bugs with it. (Marcs code works nicely!) If you find any problems with it, please comment or edit or something :)
Not pretty, but I've had this for a while :
private string getNextFileName(string fileName)
{
string extension = Path.GetExtension(fileName);
int i = 0;
while (File.Exists(fileName))
{
if (i == 0)
fileName = fileName.Replace(extension, "(" + ++i + ")" + extension);
else
fileName = fileName.Replace("(" + i + ")" + extension, "(" + ++i + ")" + extension);
}
return fileName;
}
Assuming the files already exist:
File.txt
File(1).txt
File(2).txt
the call getNextFileName("File.txt") will return "File(3).txt".
Not the most efficient because it doesn't use binary search, but should be ok for small file count. And it doesn't take race condition into account...
If checking if the file exists is too hard you can always just add a date and time to the file name to make it unique:
FileName.YYYYMMDD.HHMMSS
Maybe even add milliseconds if necessary.
If the format doesn't bother you then you can call:
try{
string tempFile=System.IO.Path.GetTempFileName();
string file=System.IO.Path.GetFileName(tempFile);
//use file
System.IO.File.Delete(tempFile);
}catch(IOException ioe){
//handle
}catch(FileIOPermission fp){
//handle
}
PS:- Please read more about this at msdn before using.
/// <summary>
/// Create a unique filename for the given filename
/// </summary>
/// <param name="filename">A full filename, e.g., C:\temp\myfile.tmp</param>
/// <returns>A filename like C:\temp\myfile633822247336197902.tmp</returns>
public string GetUniqueFilename(string filename)
{
string basename = Path.Combine(Path.GetDirectoryName(filename),
Path.GetFileNameWithoutExtension(filename));
string uniquefilename = string.Format("{0}{1}{2}",
basename,
DateTime.Now.Ticks,
Path.GetExtension(filename));
// Thread.Sleep(1); // To really prevent collisions, but usually not needed
return uniquefilename;
}
As DateTime.Ticks has a resolution of 100 nanoseconds, collisions are extremely unlikely. However, a Thread.Sleep(1) will ensure that, but I doubt that it's needed
Insert a new GUID into the file name.
I must throw my 2-cents in. This is how I did it and it works for my use.
private static string IterateFileName(string fileName)
{
if (!File.Exists(fileName)) return fileName;
FileInfo fi = new FileInfo(fileName);
string ext = fi.Extension;
string name = fi.FullName.Substring(0, fi.FullName.Length - ext.Length);
int i = 2;
while (File.Exists($"{name}_{i}{ext}"))
{
i++;
}
return $"{name}_{i}{ext}";
}
The idea is to get a list of the existing files, parse out the numbers, then make the next highest one.
Note: This is vulnerable to race conditions, so if you have more than one thread creating these files, be careful.
Note 2: This is untested.
public static FileInfo GetNextUniqueFile(string path)
{
//if the given file doesn't exist, we're done
if(!File.Exists(path))
return new FileInfo(path);
//split the path into parts
string dirName = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
//get the directory
DirectoryInfo dir = new DirectoryInfo(dir);
//get the list of existing files for this name and extension
var existingFiles = dir.GetFiles(Path.ChangeExtension(fileName + " *", fileExt);
//get the number strings from the existing files
var NumberStrings = from file in existingFiles
select Path.GetFileNameWithoutExtension(file.Name)
.Remove(0, fileName.Length /*we remove the space too*/);
//find the highest existing number
int highestNumber = 0;
foreach(var numberString in NumberStrings)
{
int tempNum;
if(Int32.TryParse(numberString, out tempnum) && tempNum > highestNumber)
highestNumber = tempNum;
}
//make the new FileInfo object
string newFileName = fileName + " " + (highestNumber + 1).ToString();
newFileName = Path.ChangeExtension(fileName, fileExt);
return new FileInfo(Path.Combine(dirName, newFileName));
}
Instead of poking the disk a number of times to find out if it has a particular variant of the desired file name, you could ask for the list of files that already exist and find the first gap according to your algorithm.
public static class FileInfoExtensions
{
public static FileInfo MakeUnique(this FileInfo fileInfo)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName);
return new FileInfo(newfileName);
}
}
public class FileUtilities
{
public string GetNextFileName(string fullFileName)
{
if (fullFileName == null)
{
throw new ArgumentNullException("fullFileName");
}
if (!File.Exists(fullFileName))
{
return fullFileName;
}
string baseFileName = Path.GetFileNameWithoutExtension(fullFileName);
string ext = Path.GetExtension(fullFileName);
string filePath = Path.GetDirectoryName(fullFileName);
var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext)
.Select(x => Path.GetFileNameWithoutExtension(x).Substring(baseFileName.Length))
.Select(x =>
{
int result;
return Int32.TryParse(x, out result) ? result : 0;
})
.Distinct()
.OrderBy(x => x)
.ToList();
var firstGap = numbersUsed
.Select((x, i) => new { Index = i, Item = x })
.FirstOrDefault(x => x.Index != x.Item);
int numberToUse = firstGap != null ? firstGap.Item : numbersUsed.Count;
return Path.Combine(filePath, baseFileName) + numberToUse + ext;
}
}
Here's one that decouples the numbered naming question from the check of the filesystem:
/// <summary>
/// Finds the next unused unique (numbered) filename.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="inUse">Function that will determine if the name is already in use</param>
/// <returns>The original filename if it wasn't already used, or the filename with " (n)"
/// added to the name if the original filename is already in use.</returns>
private static string NextUniqueFilename(string fileName, Func<string, bool> inUse)
{
if (!inUse(fileName))
{
// this filename has not been seen before, return it unmodified
return fileName;
}
// this filename is already in use, add " (n)" to the end
var name = Path.GetFileNameWithoutExtension(fileName);
var extension = Path.GetExtension(fileName);
if (name == null)
{
throw new Exception("File name without extension returned null.");
}
const int max = 9999;
for (var i = 1; i < max; i++)
{
var nextUniqueFilename = string.Format("{0} ({1}){2}", name, i, extension);
if (!inUse(nextUniqueFilename))
{
return nextUniqueFilename;
}
}
throw new Exception(string.Format("Too many files by this name. Limit: {0}", max));
}
And here's how you might call it if you are using the filesystem
var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));
private async Task<CloudBlockBlob> CreateBlockBlob(CloudBlobContainer container, string blobNameToCreate)
{
var blockBlob = container.GetBlockBlobReference(blobNameToCreate);
var i = 1;
while (await blockBlob.ExistsAsync())
{
var newBlobNameToCreate = CreateRandomFileName(blobNameToCreate,i.ToString());
blockBlob = container.GetBlockBlobReference(newBlobNameToCreate);
i++;
}
return blockBlob;
}
private string CreateRandomFileName(string fileNameWithExtension, string prefix=null)
{
int fileExtPos = fileNameWithExtension.LastIndexOf(".", StringComparison.Ordinal);
if (fileExtPos >= 0)
{
var ext = fileNameWithExtension.Substring(fileExtPos, fileNameWithExtension.Length - fileExtPos);
var fileName = fileNameWithExtension.Substring(0, fileExtPos);
return String.Format("{0}_{1}{2}", fileName, String.IsNullOrWhiteSpace(prefix) ? new Random().Next(int.MinValue, int.MaxValue).ToString():prefix,ext);
}
//This means there is no Extension for the file and its fine attaching random number at the end.
return String.Format("{0}_{1}", fileNameWithExtension, new Random().Next(int.MinValue, int.MaxValue));
}
I use this code to create a consecutive _1,_2,_3 etc.. file name everytime a file exists in the blob storage.
Hope this self iterating function may help. It works fine for me.
public string getUniqueFileName(int i, string filepath, string filename)
{
string path = Path.Combine(filepath, filename);
if (System.IO.File.Exists(path))
{
string name = Path.GetFileNameWithoutExtension(filename);
string ext = Path.GetExtension(filename);
i++;
filename = getUniqueFileName(i, filepath, name + "_" + i + ext);
}
return filename;
}
This is an answer to question in this Link, but they marked it as a duplicate, so I post my answer here.
I created this proof of concept class (may contain bugs).
More explanation in code comments.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApp
{
class Program
{
static void Main( string[] args )
{
var testFilePaths = new List<string>
{
#"c:\test\file.txt",
#"c:\test\file(1).txt",
#"c:\test\file(2).txt",
#"c:\TEST2\file(3).txt",
#"c:\test\file(5).txt",
#"c:\test\file(5)abc.txt",
#"c:\test\file(5).avi"
};
// inspect in debbuger for correct values
var withSuffix = new DecomposedFilePath( "c:\\files\\file(13).txt");
var withoutSuffix = new DecomposedFilePath( "c:\\files\\file(abc).txt");
var withExtraNumber = new DecomposedFilePath( "c:\\files\\file(34)xyz(35).txt"); // "(34)" in the middle should be ignored
DecomposedFilePath changedSuffix = withExtraNumber.ReplaceSuffix( 1999 ); // "file(34)xyz(35).txt" -> "file(34)xyz(1999).txt"
DecomposedFilePath removedSuffix = changedSuffix.ReplaceSuffix( null ); // "file(34)xyz(1999).txt" -> "file(34)xyz.txt"
var testPath = new DecomposedFilePath( "c:\\test\\file.txt");
DecomposedFilePath nextPath1 = testPath.GetFirstFreeFilePath( testFilePaths );
// update our list
testFilePaths.Add( nextPath1.FullFilePath );
DecomposedFilePath nextPath2 = testPath.GetFirstFreeFilePath( testFilePaths );
testFilePaths.Add( nextPath2.FullFilePath );
DecomposedFilePath nextPath3 = testPath.GetFirstFreeFilePath( testFilePaths );
}
}
public sealed class DecomposedFilePath
{
public DecomposedFilePath( string filePath )
{
FullFilePath = Path.GetFullPath( filePath );
}
// "c:\myfiles\file(4).txt"
public string FullFilePath { get; }
// "file" or "file(1)"
public string FileNameWithoutExt => Path.GetFileNameWithoutExtension( FullFilePath );
// "file(13)" -> "file"
public string FileNameWithoutExtAndSuffix => FileNameWithoutExt.Substring( 0, FileNameWithoutExt.Length - Suffix.Length ); // removes suffix
// ".txt"
public string Extenstion => Path.GetExtension( FullFilePath );
// "c:\myfiles"
public string DirectoryPath => Path.GetDirectoryName( FullFilePath );
// "file(23)" -> "23", file -> stirng.Empty
public string Suffix
{
get
{
// we want to extract suffix from file name, e.g. "(34)" from "file(34)"
// I am not good at regex, but I hope it will work correctly
var regex = new Regex( #"\([0-9]+\)$" );
Match match = regex.Match( FileNameWithoutExt );
if (!match.Success) return string.Empty; // suffix not found
return match.Value; // return "(number)"
}
}
// tranlates suffix "(33)" to 33. If suffix is does not exist (string.empty), returns null (int?)
public int? SuffixAsInt
{
get
{
if (Suffix == string.Empty) return null;
string numberOnly = Suffix.Substring( 1, Suffix.Length - 2 ); // remove '(' from beginning and ')' from end
return int.Parse( numberOnly );
}
}
// e.g. input is suffix: 56 then it changes file name from "file(34)" to "file(56)"
public DecomposedFilePath ReplaceSuffix( int? suffix ) // null - removes suffix
{
string strSuffix = suffix is null ? string.Empty : $"({suffix})"; // add ( and )
string path = Path.Combine( DirectoryPath, FileNameWithoutExtAndSuffix + strSuffix + Extenstion ); // build full path
return new DecomposedFilePath( path );
}
public DecomposedFilePath GetFirstFreeFilePath( IEnumerable<string> filesInDir )
{
var decomposed = filesInDir
// convert all paths to our class
.Select( x => new DecomposedFilePath( x ) )
// pick files only with the same extensionm as our base file, ignore case
.Where( x => string.Equals( Extenstion, x.Extenstion, StringComparison.OrdinalIgnoreCase) )
// pick files only with the same name (ignoring suffix)
.Where( x => string.Equals( FileNameWithoutExtAndSuffix, x.FileNameWithoutExtAndSuffix, StringComparison.OrdinalIgnoreCase) )
// with the same directory
.Where( x => string.Equals( DirectoryPath, x.DirectoryPath, StringComparison.OrdinalIgnoreCase) )
.ToList(); // create copy for easier debugging
if (decomposed.Count == 0) return this; // no name collision
int? firstFreeSuffix = Enumerable.Range( 1, int.MaxValue) // start numbering duplicates from 1
.Select( x => (int?) x) // change to int? because SuffixAsInt is of that type
.Except( decomposed.Select( x => x.SuffixAsInt) ) // remove existing suffixes
.First(); // get first free suffix
return ReplaceSuffix( firstFreeSuffix );
}
public override string ToString() => FullFilePath;
}
}
This is just a string operation; find the location in the filename string where you want to insert the number, and re-construct a new string with the number inserted. To make it re-usable, you might want to look for a number in that location, and parse it out into an integer, so you can increment it.
Please note that this in general this way of generating a unique filename is insecure; there are obvious race condition hazards.
There might be ready-made solutions for this in the platform, I'm not up to speed with C# so I can't help there.
Take a look at the methods in the Path class, specifically Path.GetFileNameWithoutExtension(), and Path.GetExtension().
You may even find Path.GetRandomFileName() useful!
Edit:
In the past, I've used the technique of attempting to write the file (with my desired name), and then using the above functions to create a new name if an appropriate IOException is thrown, repeating until successful.
This method will add a index to existing file if needed:
If the file exist, find the position of the last underscore. If the content after the underscore is a number, increase this number. otherwise add first index. repeat until unused file name found.
static public string AddIndexToFileNameIfNeeded(string sFileNameWithPath)
{
string sFileNameWithIndex = sFileNameWithPath;
while (File.Exists(sFileNameWithIndex)) // run in while scoop so if after adding an index the the file name the new file name exist, run again until find a unused file name
{ // File exist, need to add index
string sFilePath = Path.GetDirectoryName(sFileNameWithIndex);
string sFileName = Path.GetFileNameWithoutExtension(sFileNameWithIndex);
string sFileExtension = Path.GetExtension(sFileNameWithIndex);
if (sFileName.Contains('_'))
{ // Need to increase the existing index by one or add first index
int iIndexOfUnderscore = sFileName.LastIndexOf('_');
string sContentAfterUnderscore = sFileName.Substring(iIndexOfUnderscore + 1);
// check if content after last underscore is a number, if so increase index by one, if not add the number _01
int iCurrentIndex;
bool bIsContentAfterLastUnderscoreIsNumber = int.TryParse(sContentAfterUnderscore, out iCurrentIndex);
if (bIsContentAfterLastUnderscoreIsNumber)
{
iCurrentIndex++;
string sContentBeforUnderscore = sFileName.Substring(0, iIndexOfUnderscore);
sFileName = sContentBeforUnderscore + "_" + iCurrentIndex.ToString("000");
sFileNameWithIndex = sFilePath + "\\" + sFileName + sFileExtension;
}
else
{
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
else
{ // No underscore in file name. Simple add first index
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
return sFileNameWithIndex;
}
I did it like this:
for (int i = 0; i <= 500; i++) //I suppose the number of files will not pass 500
{ //Checks if C:\log\log+TheNumberOfTheFile+.txt exists...
if (System.IO.File.Exists(#"C:\log\log"+conta_logs+".txt"))
{
conta_logs++;//If exists, then increment the counter
}
else
{ //If not, then the file is created
var file = System.IO.File.Create(#"C:\log\log" + conta_logs + ".txt");
break; //When the file is created we LEAVE the *for* loop
}
}
I think this version is not so hard like the others, and It's a straightforward answer for what the user wanted.
If you need just a unique file name, so, how about this?
Path.GetRandomFileName()
I ran into this problem and, since none of the other answers seemed to have solved it in the way I wanted to, I did it on my own.
static string CheckIfFileExists(string filePath)
{
if (File.Exists(filePath))
{
string parentDir = Directory.GetParent(filePath).FullName;
string fileName = new DirectoryInfo(filePath).Name;
string extension = Path.GetExtension(fileName);
fileName = Path.GetFileNameWithoutExtension(fileName);
if (CheckIfFileNameHasIndex(fileName))
{
string strIndex = fileName[(fileName.LastIndexOf('(')+1)..fileName.LastIndexOf(')')]; //range
int index = int.Parse(strIndex);
index++;
fileName = fileName.Substring(0, fileName.LastIndexOf('(')) + "(" + index + ')';
filePath = Path.Combine(parentDir, fileName + extension);
return CheckIfFileExists(filePath);
}
else
{
fileName = fileName + " (1)";
filePath = Path.Combine(parentDir, fileName + extension);
return CheckIfFileExists(filePath);
}
}
return filePath;
}
//checks if filename has an index (e.g. "file(2).jpg")
static bool CheckIfFileNameHasIndex(string fileName)
{
bool isSuccessful = false;
if (fileName.LastIndexOf('(')!=-1 && fileName.LastIndexOf(')')!=-1)
{
string index = fileName[(fileName.LastIndexOf('(')+1)..fileName.LastIndexOf(')')]; //range
int result;
isSuccessful = int.TryParse(index, out result);
}
return isSuccessful;
}
The method CheckIfFileExists is recursive, so in theory it should be able to handle a potentially unlimited number of duplicates (e.g. "file (3484939).txt"). Of course, in reality, what happens is that the maximum imposed filename length of your operating system and stuff like eventually become a bottleneck.
I have written a method that returns "next" file name with number.
Supports numbering from 1 to 99.
Examples:
C:\Recovery.txt → C:\Recovery1.txt
C:\Recovery1.txt → C:\Recovery2.txt
How to call:
while (File.Exists( path ))
path = NextFileNum( path );
internal static string NextFileNum( string path )
{
string filename = Path.GetFileNameWithoutExtension( path );
string ext = Path.GetExtension( path );
string dir = Path.GetDirectoryName( path );
for (int i = 99; i > 0; i--)
{
if (filename.EndsWith( i.ToString() ))
{
string suffix = ( i + 1 ).ToString();
filename = filename.Substring( 0, filename.Length - suffix.Length ) + suffix;
return Path.Combine( dir, filename + ext );
}
}
filename = filename + "1";
return Path.Combine( dir, filename + ext );
}
public static string MakeUniqueFilePath(string filePath)
{
if (!File.Exists(filePath)) return filePath;
var directory = Path.GetDirectoryName(filePath);
var fileName = Path.GetFileNameWithoutExtension(filePath);
var fileExt = Path.GetExtension(filePath);
var i = 1;
do
{
filePath = Path.Combine(directory, fileName + "(" + i + ")" + fileExt);
i++;
} while (File.Exists(filePath));
return filePath;
}
Returns files like so:
test.txt
test(1).txt
test(2).txt
etc.
Notes:
Can handle filenames without extensions
Can Handle directories included in the file path.
Does not handle file creation race conditions when saving.