C#:如何通过添加数字来创建唯一的文件名?
我想创建一个方法,该方法将文件名作为C#:如何通过添加数字来创建唯一的文件名?,c#,unique,filenames,C#,Unique,Filenames,我想创建一个方法,该方法将文件名作为字符串或文件信息并在文件名中添加递增的数字(如果文件存在)。但我不知道该如何以一种好的方式做到这一点 例如,如果我有这个FileInfo var file = new FileInfo(@"C:\file.ext"); 如果C:\file.ext,我希望该方法为我提供一个带有C:\file 1.ext的新FileInfo 如果存在C:\file 1.ext,则为C:\file 2.ext,依此类推。大概是这样的: public FileInfo MakeUn
字符串
或文件信息
并在文件名中添加递增的数字(如果文件存在)。但我不知道该如何以一种好的方式做到这一点
例如,如果我有这个FileInfo
var file = new FileInfo(@"C:\file.ext");
如果C:\file.ext,我希望该方法为我提供一个带有C:\file 1.ext的新FileInfo
如果存在C:\file 1.ext,则为C:\file 2.ext,依此类推。大概是这样的:
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.
}
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
}
}
这只是一个字符串操作;在文件名字符串中找到要插入数字的位置,然后用插入的数字重新构造一个新字符串。为了使其可重用,您可能需要在该位置查找一个数字,并将其解析为一个整数,以便可以对其进行递增 请注意,通常这种生成唯一文件名的方法是不安全的;有明显的危险
平台上可能有现成的解决方案,我没有跟上C语言的速度,所以我无法帮助解决这个问题。看看类中的方法,特别是 你甚至会发现它很有用 编辑: 在过去,我使用的技术是尝试编写文件(使用我想要的名称),然后在抛出适当的
IOException
时使用上述函数创建一个新名称,重复直到成功
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);
}
}
很明显,这很容易受到其他答案中提到的竞争条件的影响。如果很难检查文件是否存在,则始终可以在文件名中添加日期和时间以使其唯一: FileName.YYYYMMDD.HHMMSS
如果需要,甚至可以添加毫秒。如果格式不影响您,您可以调用:
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:-请在使用之前阅读更多关于此的信息。在文件名中插入新的GUID。方法是获取现有文件的列表,解析出数字,然后生成下一个最高的 注意:这很容易受到竞争条件的影响,因此如果有多个线程创建这些文件,请小心 注2:这是未经测试的
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));
}
这里有很多好建议。我最终使用了一种由in编写的方法。将其重新格式化一点点,并添加了另一种方法,使其“从外部”更易于使用。结果如下:
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);
}
到目前为止,只对它进行了部分测试,但如果我发现它有任何缺陷,将进行更新。(s代码运行得很好!)如果您发现它有任何问题,请发表评论或进行编辑或其他操作:)//
///为给定文件名创建唯一的文件名
///
///完整文件名,例如C:\temp\myfile.tmp
///像C:\temp\myfile633822247336197902.tmp这样的文件名
公共字符串GetUniqueFilename(字符串文件名)
{
字符串basename=Path.Combine(Path.GetDirectoryName(文件名),
GetFileNameWithoutExtension(filename));
string uniquefilename=string.Format(“{0}{1}{2}”,
基本名称,
DateTime.Now.Ticks,
GetExtension(文件名));
//Thread.Sleep(1);//真正防止冲突,但通常不需要
返回唯一文件名;
}
因此,碰撞是极不可能的。然而,Thread.Sleep(1)可以确保这一点,但我怀疑是否需要它来代替多次戳磁盘以确定它是否具有所需文件名的特定变体,您可以请求已经存在的文件列表,并根据您的算法找到第一个间隙
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;
}
}
下面是一个将编号命名问题与文件系统检查分离的问题:
/// <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));
}
不漂亮,但我已经有一段时间了:
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;
}
假设文件已经存在:
- File.txt
- 文件(1).txt
- 文件(2).txt
不是最有效的,因为它不使用二进制搜索,但是对于小文件计数应该是可以的。而且它不考虑竞争条件…如果需要,此方法将向现有文件添加索引: 如果文件存在,请查找最后一个下划线的位置。如果下划线后面的内容是数字,请增加此数字。否则,添加第一个索引。重复此操作,直到找到未使用的文件名
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;
}
我是这样做的:
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.
}
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
}
}
for(int i=0;iprivate异步任务CreateBlockBlob(CloudBlobContainer容器,字符串blobNameToCreate)
{
var blockBlob=container.GetBlockBlobReference(blobNameToCreate);
var i=1;
while(等待blockBlob.ExistsAsync())
{
var newBlobNameToCreate=CreateRandomFileName(blobNameToCreate,i.ToString());
blockBlob=container.GetBlockBlobReference(newBlobNameToCreate);
i++;
}
返回blockBlob;
}
私有字符串CreateRandomFileName(字符串文件名带扩展名,字符串前缀=null)
{
int fileExtPos=fileNameWithExtension.LastIndexOf(“.”,StringComparison.Ordinal);
如果(fileExtPos>=0)
{
var ext=fileNameWithExtension.Substring(fileExtPos,fileNameWithExtension.Length-fileExtPos);
var fileName=fileNameWithExtension.Substring(0,fileExtPos);
返回String.Format(“{0}{1}{2}”,文件名,String.IsNullOrWhiteSpace(前缀)?new Random().Next(int.MinValue,int.MaxValue).ToString():前缀,ext);
}
//这意味着该文件没有扩展名,其末尾没有随机数。
返回String.Format(“{0}{1}”,文件名带扩展名,new Random().Next(int.MinValue,int.MaxValue));
}
我使用此代码创建一个连续的_1、_2、_3等文件名,每次blob存储中存在一个文件。希望此自迭代函数能有所帮助。它对我很好
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;
}
如果您只需要一个唯一的文件名,那么,这个怎么样
Path.GetRandomFileName()
这是一个
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;
}
}
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}";
}