C# 保存多个同名文件

C# 保存多个同名文件,c#,.net,wpf,unique,filenames,C#,.net,Wpf,Unique,Filenames,我正在开发一个C#WPF应用程序,它可以进行一些数据处理,并以.csv格式输出文件。我试图实现的是,每当我的程序运行时,它都会生成一个test.csv输出文件,但是当我使用新数据再次运行时。。新的结果文件将覆盖旧文件。因此,我试图做一些事情,使每个创建的文件名是唯一的。例如,test.csv,然后是test1.csv,test2.csv等等。我想出了下面的片段,但我认为逻辑不正确,我不知道如何做到这一点。任何帮助都将不胜感激 filename = "C:\\test.csv"; if (File

我正在开发一个C#WPF应用程序,它可以进行一些数据处理,并以
.csv
格式输出文件。我试图实现的是,每当我的程序运行时,它都会生成一个
test.csv
输出文件,但是当我使用新数据再次运行时。。新的结果文件将覆盖旧文件。因此,我试图做一些事情,使每个创建的文件名是唯一的。例如,
test.csv
,然后是
test1.csv
test2.csv
等等。我想出了下面的片段,但我认为逻辑不正确,我不知道如何做到这一点。任何帮助都将不胜感激

filename = "C:\\test.csv";
if (File.Exists(filename))
{
    int count = 0;
    for (int i = 0; i < 10; i++)
    {    
        filename = "C:\\test" + count + ".csv";
        count++;
    }
}
filename=“C:\\test.csv”;
if(File.Exists(filename))
{
整数计数=0;
对于(int i=0;i<10;i++)
{    
filename=“C:\\test”+count+“.csv”;
计数++;
}
}

在原始代码中,您只需执行一次检查。尝试(注意
,同时
):

如果愿意,您可以将
while
替换为
for
循环:

for(int count = 0; File.Exists(filename); count++)
    filename = "C:\\test" + count + ".csv";                            
更新:正如@eFloh所指出的(参见注释),当上述代码的两个实例同时工作时,可能会出现问题

这是因为在循环和实际的写磁盘操作之间有一个微小的窗口,所以其他进程可能只是在中间,写一个同名的文件,覆盖另一个程序正在创建的文件或造成错误。 一种可能的解决方案是在循环执行时(如果文件存在,则停止执行)向目录写入一个特殊文件,以确保两个正在运行的实例不会冲突

本质上,该文件将向程序发出另一个程序正在执行该循环的信号,因此另一个程序将等待另一个程序终止

下面代码的解释:使用
块的
正在尝试在目录中创建名为
lock\u file
的文件

  • FileMode.OpenOrCreate
    可以打开文件(如果已经存在)或创建文件(如果不存在)
  • FileAccess.Write
    make可以写入
    lock\u文件
  • FileShare.Read
    使使用等待,直到罚款被其他进程关闭,因此程序将在需要时等待
  • 最后,
    FileOptions.DeleteOnClose
    ,使
    在退出时使用
    块删除
    lock\u文件。这一点非常重要,因为否则,
    lock_文件
    将永远停留在那里,使此块永远等待删除此文件
总结一下(同样,这篇文章的功劳归@eFloh):


您可以通过LINQ进行检查,直到找到可用的文件名

var pathFormat = "c:\\test{0}.csv";

var maxIndex = Enumerable.Range(0, int.MaxValue)
    .SkipWhile(x => File.Exists(string.Format(pathFormat, x)))
    .First();

var csvPath = string.Format(pathFormat, maxIndex);
Console.WriteLine(csvPath);

或者,您可以应用不同的文件命名约定,例如:[file name]+timestamp以确保其唯一性,因此不需要重复名称检查:

    DateTime _dt = DateTime.Now;
    string filename = @"C:\test_" + 
        _dt.Year.ToString() +
        _dt.Month.ToString() + 
        _dt.Month.ToString() + 
        _dt.Day.ToString() + "_" +
        _dt.Hour.ToString()+
        _dt.Minute.ToString()+
        _dt.Second.ToString() +".csv";
}
必要时,您可以通过添加
\u dt.millisond.ToString()
对其进行扩展。或者,您可以向文件名添加GUID以确保其唯一性,而不是使用时间戳


希望这会有所帮助。

因为每当您检查并尝试写入时,可能有人同时创建了另一个文件,您应该尝试在循环中打开以避免竞争条件:

            int tryCount = 1;
            string pathFormat = "c:\\test{0}.csv";

            while (tryCount <= MAX_TRYCOUNT)
            {
                try
                {
                    using (FileStream fs = File.Open(String.Format(CultureInfo.InvariantCulture, pathFormat, tryCount), FileMode.CreateNew))
                    {
                        //fs.Write or open StreamWriter etc.
                    }
                }
                catch (IOException)
                {
                    /* try next filename */
                }
                catch (Exception ex)
                {
                    /* other unexpected error, escape. */
                    throw;
                }

                tryCount++;
            }

            if (tryCount == MAX_TRYCOUNT)
            {
                throw new IOException("No free filename available.");
            }

但这只会使竞争条件朝着正确检查锁文件的方向移动,您也需要在此处尝试捕获。:)

使用
std::to_string(count)
。您还需要在for循环中检查
是否存在(File.Exists(filename))
after循环。一旦这是错误的,那么你需要打破循环。为什么不为每个文件创建一个新的Guid呢?在while之前初始化计数,去掉for循环,你就可以将while
更改为for
并将
int count=0
以及
count++
放入it@DmitryBychenko为什么?除了减少代码行数之外,没有其他好处。它看起来(至少对我来说)比
while
循环可读性稍差。就我个人而言,我会选择
while
循环,而不是其他任何一个选项。
while
更符合我的口味。尽管如此,我还是为
解决方案添加了
。该解决方案在检查文件是否存在和实际创建文件之间存在竞争条件。这有点过分了(实际上不需要LINQ)。一个简单的
for
while
循环就足够了。我知道,只是想显示不同的选项@因此,我没有说这是一个糟糕的答案。不是。它只是比
while
for
解决方案稍微大一点(虽然这是一种观点,但可读性较差)。
    DateTime _dt = DateTime.Now;
    string filename = @"C:\test_" + 
        _dt.Year.ToString() +
        _dt.Month.ToString() + 
        _dt.Month.ToString() + 
        _dt.Day.ToString() + "_" +
        _dt.Hour.ToString()+
        _dt.Minute.ToString()+
        _dt.Second.ToString() +".csv";
}
            int tryCount = 1;
            string pathFormat = "c:\\test{0}.csv";

            while (tryCount <= MAX_TRYCOUNT)
            {
                try
                {
                    using (FileStream fs = File.Open(String.Format(CultureInfo.InvariantCulture, pathFormat, tryCount), FileMode.CreateNew))
                    {
                        //fs.Write or open StreamWriter etc.
                    }
                }
                catch (IOException)
                {
                    /* try next filename */
                }
                catch (Exception ex)
                {
                    /* other unexpected error, escape. */
                    throw;
                }

                tryCount++;
            }

            if (tryCount == MAX_TRYCOUNT)
            {
                throw new IOException("No free filename available.");
            }
string lockFile = Path.Combine(PATH_OF_EXPORTDIR,"SingleInstanceCanWrite");
using(new FileStream(lockFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 8 * 1024, FileOptions.DeleteOnClose))
{
   /* find next free filename, open write and close */
}