2015-06-08 4 views
2

Я работаю над приложением 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++; 
    } 
} 
+3

Используйте 'std :: to_string (count)'. –

+2

Вы также устарели, чтобы проверить 'if (File.Exists (filename))' после цикла в вашем цикле for. Когда это будет ложно, вам нужно выйти из цикла. – NathanOliver

+1

Почему бы не создать «новое руководство» для каждого файла? – Max

ответ

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

Update: Как @eFloh показано (см комментарии), может возникнуть проблема, когда два экземпляра вышеуказанного кода работают одновременно.

Это связано с тем, что между циклом и фактической операцией write-to-disk существует небольшое окно, поэтому другой процесс может просто попасть в середину и записать файл с тем же именем, перезаписывая файл, создаваемый другой программой или возникновения ошибок.

Одним из возможных решений является запись специального файла в каталог при выполнении цикла (и остановка выполнения - такой файл существует), чтобы гарантировать отсутствие конфликтов между двумя работающими экземплярами.

По существу, этот файл будет сигнализировать программе о том, что другая программа выполняет этот цикл, поэтому другая программа должна ждать, пока другой не завершится.

Объяснение кода ниже:using блок пытается создать файл с именем lock_file в каталоге.

  • FileMode.OpenOrCreate позволяет открывать файл (если он уже существует) или создавать его (если это не так).
  • FileAccess.Write делает можно написать 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 
} 
+0

инициализируйте счет до времени и избавитесь от цикла for, и вы хорошо пойдете –

+0

Измените 'while' на' for' и поместите 'int count = 0', а также' count ++' в него –

+0

@DmitryBychenko Почему ? Для этого нет никакой дополнительной выгоды, кроме как уменьшить количество строк кода. Он также появляется (по крайней мере для меня) немного менее читаемым, чем цикл while. Лично я бы выбрал цикл while в любом из других представленных вариантов. –

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); 
+1

Это немного перебор для этой цели (и на самом деле не требует LINQ). Простой цикл 'for' или' while' будет достаточным. –

+0

Я знаю, просто хочу показать разные варианты. @EBrown – dotctor

+0

Следовательно, почему я не сказал, что это * плохой ответ *. Это не. Это немного больше (и менее читаемо, хотя это мнение), чем решение 'while' или' for'. –

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 в имени файла, чтобы обеспечить его уникальность.

Надеюсь, это поможет.

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

, но это только перемещает состояние гонки в стороне правильно проверок локировок, вы будете нуждаться в примерочный улове здесь тоже. :)

Смежные вопросы