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#的速度,所以我无法帮助解决这个问题。这只是一个字符串操作;在文件名字符串中找到要插入数字的位置,然后用插入的数字重新构造一个新字符串。为了使其可重用,您可能需要在该位置查找一个数字,并将其解析为一个整数,以便可以对其进行递增 请注意,通常这种生成唯一文件名的方法是不安全的;有明显的危险
平台上可能有现成的解决方案,我没有跟上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);
}
}
你甚至会发现它很有用
编辑:
在过去,我使用的技术是尝试编写文件(使用我想要的名称),然后在抛出适当的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
如果需要,甚至可以添加毫秒。如果很难检查文件是否存在,则始终可以在文件名中添加日期和时间以使其唯一: 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:-请在使用前阅读更多信息。如果格式不影响您,您可以拨打:
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。在文件名中插入新的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));
}
这样做的目的是获取现有文件的列表,解析出数字,然后生成下一个最高的数字 注意:这很容易受到竞争条件的影响,因此如果有多个线程创建这些文件,请小心 注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代码工作得很好!)如果您发现它有任何问题,请发表评论或编辑或其他:)这里有很多好的建议。我最终使用了一种由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)将确保这一点,但我怀疑是否需要它
///为给定文件名创建唯一的文件名
///
///完整文件名,例如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,
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
}
}
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));
}
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}";
}