Есть ли функция для генерации имени файла скопированного файла? - PullRequest
4 голосов
/ 11 февраля 2010

Есть ли в C # какие-либо функции для создания нового имени для скопированного элемента?
Например, если у меня есть строка с именем «Папка», мне нужна функция для создания строки «Копия папки» .... и с учетом строки «Копия папки» функция должна создать «Копия папки» "и так далее ...

Ответы [ 4 ]

6 голосов
/ 11 февраля 2010

Вы можете написать цикл, например так:

string baseName = @"C:\Parent\Copy of Folder", actualName = baseName;
int index = 0;

while(File.Exists(actualName) || Directory.Exists(actualName))
    actualName = baseName + " (" + (++index) + ")";

В зависимости от вашего варианта использования, вы, вероятно, должны поместить это в static служебный метод.

4 голосов
/ 11 февраля 2010

@ SLaks имеет базовый алгоритм, но вы должны иметь в виду условия гонки. Файл может быть создан каким-либо другим потоком или процессом во время между проверкой File.Exists и созданием файла. Вот схема решения, основанного на алгоритме SLak:

FileStream fileCopy;
while(File.Exists(actualName) || Directory.Exists(acutalName))
{
  actualName = baseName + " (" + (++index) + ")"; 
  try
  {
    fileCopy = new FileStream(actualName, FileMode.CreateNew);
  }
  catch (IOException)
  {
     if (!File.Exists(actualName))
     {
       throw;
     }
  }
}

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

Вы можете просто использовать, генерирует ли операция «новый FileStream» исключение в качестве условия цикла, но я избегаю генерировать исключения для условий без ошибок. Сложнее воспользоваться преимуществами поведения «ловить при выбросе» в отладчике, а исключения являются дорогостоящими в вычислительном отношении. Я предполагаю, что в контексте вашего приложения не является ошибкой иметь файл в файловой системе с тем же именем, которое вы хотели бы использовать для своей копии. Если я ошибаюсь, то, на мой взгляд, уместно исключение, если файл существует.

0 голосов
/ 11 февраля 2010

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

Короткий образец будет выглядеть так:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        string file = @"C:\tmp\test.txt";
        try
        {
            SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT();

            fileOp.wFunc = FO_Func.FO_COPY;
            fileOp.fFlags = (ushort)(FILEOP_FLAGS.FOF_RENAMEONCOLLISION);

            // file names need double-null termination
            fileOp.pFrom = file + '\0' + '\0';

            // use the same name as target for demo purpose
            fileOp.pTo = file + '\0' + '\0';

            int hRes = SHFileOperation(ref fileOp);
            if (hRes != 0)
            {
                throw new Win32Exception(hRes);
            }
        }
        catch (Exception ex)
        {
            Trace.WriteLine(ex);
        }
    }

    [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
    static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
    struct SHFILEOPSTRUCT
    {
        public IntPtr hwnd;
        public FO_Func wFunc;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pFrom;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pTo;
        public ushort fFlags;
        public Int32 fAnyOperationsAborted;
        public IntPtr hNameMappings;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszProgressTitle;
    }

    public enum FO_Func : uint
    {
        FO_MOVE = 0x0001,
        FO_COPY = 0x0002,
        FO_DELETE = 0x0003,
        FO_RENAME = 0x0004,
    }

    [Flags]
    public enum FILEOP_FLAGS : ushort
    {
        FOF_MULTIDESTFILES = 0x0001,
        FOF_CONFIRMMOUSE = 0x0002,
        FOF_SILENT = 0x0004,  
        FOF_RENAMEONCOLLISION = 0x0008,
        FOF_NOCONFIRMATION = 0x0010,  
        FOF_WANTMAPPINGHANDLE = 0x0020,  
        FOF_ALLOWUNDO = 0x0040,
        FOF_FILESONLY = 0x0080,  
        FOF_SIMPLEPROGRESS = 0x0100,  
        FOF_NOCONFIRMMKDIR = 0x0200,  
        FOF_NOERRORUI = 0x0400,  
        FOF_NOCOPYSECURITYATTRIBS = 0x0800,  
        FOF_NORECURSION = 0x1000,  
        FOF_NO_CONNECTED_ELEMENTS = 0x2000,  
        FOF_WANTNUKEWARNING = 0x4000,  
        FOF_NORECURSEREPARSE = 0x8000, 
    }
}

Используя флаг FOF_WANTMAPPINGHANDLE, также можно получить сгенерированное имя целевого файла. Для дальнейшего чтения и более сложных примеров см. Эту замечательную статью о CodeProject:

C # делает Shell, часть 2

0 голосов
/ 11 февраля 2010

Мне никогда не нравился префикс «Копия», используемый Microsoft, потому что копии не сортируются рядом с оригиналами. Microsoft в Windows 7 теперь согласна со мной (что всегда приятно), поскольку вместо префикса она использует суффикс, что-то вроде «Папка - Копировать», «Папка - Копировать (2)» и т. Д.

...