保存具有相同名称的多个文件

时间:2015-06-08 14:43:44

标签: c# .net wpf unique filenames

我正在开发一个C#WPF应用程序,它以.csv格式进行一些数据处理和输出文件。我想要实现的是,每次我的程序运行时,它都会生成一个test.csv输出文件,但是当我再次运行新数据时...新的结果文件将覆盖旧文件。所以我试图做一些事情,以便每个创建的文件名都是唯一的。例如,test.csv,然后test1.csvtest2.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++;
    }
}

4 个答案:

答案 0 :(得分:7)

在原始代码中,您只进行一次检查。试试(注意while):

filename = "C:\\test.csv";
int count = 0;

while (File.Exists(filename))
{
    count++;
    filename = "C:\\test" + count + ".csv";                            
}

//save file goes here

如果您愿意,可以使用此while循环替换for

for(int count = 0; File.Exists(filename); count++)
    filename = "C:\\test" + count + ".csv";                            

更新: @eFloh 指示(请参阅注释)时,上述代码的两个实例同时工作时可能会出现问题。

这是因为在循环和实际的写入磁盘操作之间有一个小窗口,因此其他进程可能只是在中间并写入一个具有相同名称的文件,覆盖该文件的其他程序正在创造或导致错误。

一种可能的解决方案是在循环执行时将特殊文件写入目录(并暂停执行是否存在此类文件),以确保没有两个正在运行的实例发生冲突。

基本上,这个文件会向程序发出另一个程序执行此循环的信号 - 所以另一个程序应该等到另一个程序终止。

以下代码说明: using尝试在目录中创建名为lock_file的文件。

  • FileMode.OpenOrCreate可以打开文件(如果已经存在)或创建文件(如果没有)。
  • FileAccess.Write make可以编写lock_file
  • FileShare.Read使用等待,直到其他进程 关闭 罚款 - 所以程序会在需要时等待。
  • 最后,FileOptions.DeleteOnClose会使using块在退出时删除lock_file。这非常重要,否则lock_file将永远留在那里,使此块永远等待删除此文件。

总结(再次,这篇文章归功于 @eFloh ):

using(new FileStream("C:\\lock_file", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 4096, FileOptions.DeleteOnClose))
{
    //loop and saving goes here
}

答案 1 :(得分:2)

您可以检查,直到找到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);

答案 2 :(得分:1)

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

    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";
}

必要时,您可以通过添加_dt.Millisecond.ToString()来扩展它。或者,您可以将GUID添加到文件名以确保其唯一性,而不是使用时间戳。

希望这可能会有所帮助。

答案 3 :(得分:1)

因为每当你检查然后尝试写,你可能有人在此期间创建了另一个文件,你应该尝试在循环中打开以逃避竞争条件:

            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 */
}

但是这只会使竞争条件朝着正确检查锁定文件的方向移动,你也需要一个try-catch。 :)