Как я могу завершить этот пример, используя LINQ и разбор строк? - PullRequest
4 голосов
/ 24 декабря 2009

Я пытаюсь написать простую программу, которая будет сравнивать файлы в отдельных папках. В настоящее время я использую LINQ to Objects для анализа папки и хотел бы также включить информацию, извлеченную из строки, в свой набор результатов.

Вот что у меня есть:

FileInfo[] fileList = new DirectoryInfo(@"G:\Norton Backups").GetFiles();

var results = from file in fileList
              orderby file.CreationTime
              select new { file.Name, file.CreationTime, file.Length };

foreach (var x in results)
    Console.WriteLine(x.Name);

Это производит:

AWS025.sv2i
AWS025_C_Drive038.v2i
AWS025_C_Drive038_i001.iv2i
AWS025_C_Drive038_i002.iv2i
AWS025_C_Drive038_i003.iv2i
AWS025_C_Drive038_i004.iv2i
AWS025_C_Drive038_i005.iv2i    
...

Я бы хотел изменить запрос LINQ так, чтобы:

  • Включает только фактические «резервные» файлы (вы можете указать файлы резервных копий из-за _C_Drive038 в приведенных выше примерах, хотя 038 и, возможно, буква диска могут измениться).
  • Я хочу включить поле, если файл является «основным» резервным файлом (т. Е. У него нет _i0XX в конце имени файла).
  • Я хочу включить «номер изображения» файла (например, в данном случае это 038).
  • Я хочу включить номер приращения, если это приращение базового файла (например, 001 будет номером приращения)

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

var results = from file in fileList
              let IsMainBackup = \\ ??
              let ImageNumber = \\ ??
              let IncrementNumber = \\ ??
              where \\ it is a backup file.
              orderby file.CreationTime
              select new { file.Name, file.CreationTime, file.Length, 
                           IsMainBackup, ImageNumber, IncrementNumber };

При поиске ImageNumber и IncrementNumber, я хотел бы предположить, что местоположение этих данных не всегда фиксировано, то есть я хотел бы узнать о хорошем способе анализа этого (если это требует RegEx, пожалуйста, объясните, как я могу его использовать).

ПРИМЕЧАНИЕ. Большая часть моего предыдущего опыта разбора текста связана с использованием строковых функций на основе местоположения, таких как LEFT, RIGHT или MID. Я бы не стал прибегать к ним, если есть лучший способ.

Ответы [ 2 ]

6 голосов
/ 24 декабря 2009

Использование регулярных выражений:

    Regex regex = new Regex(@"^.*(?<Backup>_\w_Drive(?<ImageNumber>\d+)(?<Increment>_i(?<IncrementNumber>\d+))?)\.[^.]+$");
    var results = from file in fileList
                  let match = regex.Match(file.Name)
                  let IsMainBackup = !match.Groups["Increment"].Success
                  let ImageNumber = match.Groups["ImageNumber"].Value
                  let IncrementNumber = match.Groups["IncrementNumber"].Value
                  where match.Groups["Backup"].Success
                  orderby file.CreationTime
                  select new { file.Name, file.CreationTime, file.Length,
                               IsMainBackup, ImageNumber, IncrementNumber };

Вот описание регулярного выражения:

^                   Start of string.
.*                  Allow anything at the start.
(?<Backup>...)      Match a backup description (explained below).
\.                  Match a literal period.
[^.]+$              Match the extension (anything except periods).
$                   End of string.

Резервная копия:

_\w_Drive           A literal underscore, any letter, another underscore, then the string "Drive".
(?<ImageNumber>\d+) At least one digit, saved as ImageNumber.
(?<Increment>...)?  An optional increment description.

Инкремент:

_i                      A literal underscore, then the letter i.
(?<IncrementNumber>\d+) At least one digit, saved as IncrementNumber.

Вот код теста, который я использовал:

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        FileInfo[] fileList = new FileInfo[] {
            new FileInfo("AWS025.sv2i"),
            new FileInfo("AWS025_C_Drive038.v2i"),
            new FileInfo("AWS025_C_Drive038_i001.iv2i"),
            new FileInfo("AWS025_C_Drive038_i002.iv2i"),
            new FileInfo("AWS025_C_Drive038_i003.iv2i"),
            new FileInfo("AWS025_C_Drive038_i004.iv2i"),
            new FileInfo("AWS025_C_Drive038_i005.iv2i")
        };

        Regex regex = new Regex(@"^.*(?<Backup>_\w_Drive(?<ImageNumber>\d+)(?<Increment>_i(?<IncrementNumber>\d+))?)\.[^.]+$");
        var results = from file in fileList
                      let match = regex.Match(file.Name)
                      let IsMainBackup = !match.Groups["Increment"].Success
                      let ImageNumber = match.Groups["ImageNumber"].Value
                      let IncrementNumber = match.Groups["IncrementNumber"].Value
                      where match.Groups["Backup"].Success
                      orderby file.CreationTime
                      select new { file.Name, file.CreationTime,
                                   IsMainBackup, ImageNumber, IncrementNumber };

        foreach (var x in results)
        {
            Console.WriteLine("Name: {0}, Main: {1}, Image: {2}, Increment: {3}",
                x.Name, x.IsMainBackup, x.ImageNumber, x.IncrementNumber);
        }
    }
}

А вот вывод, который я получаю:

Name: AWS025_C_Drive038.v2i, Main: True, Image: 038, Increment:
Name: AWS025_C_Drive038_i001.iv2i, Main: False, Image: 038, Increment: 001
Name: AWS025_C_Drive038_i002.iv2i, Main: False, Image: 038, Increment: 002
Name: AWS025_C_Drive038_i003.iv2i, Main: False, Image: 038, Increment: 003
Name: AWS025_C_Drive038_i004.iv2i, Main: False, Image: 038, Increment: 004
Name: AWS025_C_Drive038_i005.iv2i, Main: False, Image: 038, Increment: 005
5 голосов
/ 24 декабря 2009

Было немного весело найти хороший ответ на этот вопрос :)

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

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        //Application.Run(new Form1());

        GetBackupFiles(@"c:\temp\backup files");
    }

    static void GetBackupFiles(string path)
    {
        FileInfo[] fileList = new DirectoryInfo(path).GetFiles("*_Drive*.*v2i");

        var results = from file in fileList
                      orderby file.CreationTime
                      select new 
                      {  file.Name
                        ,file.CreationTime
                        ,file.Length 
                        ,IsMainBackup = file.Extension.ToLower() == ".v2i"
                        ,ImageNumber = Regex.Match(file.Name, @"drive([\d]{0,5})", RegexOptions.IgnoreCase).Groups[1]
                        ,IncrementNumber = parseNumber( Regex.Match(file.Name, @"_i([\d]{0,5})\.iv2i", RegexOptions.IgnoreCase).Groups[1])
                      };

        foreach (var x in results)
            Console.WriteLine(x.Name);
    }

    static int? parseNumber(object num)
    {
        int temp;
        if (num != null && int.TryParse(num.ToString(), out temp))
            return temp;
        return null;
    }
}

Обратите внимание, что с регулярными выражениями я предполагаю некоторую согласованность в именах файлов, если они будут отклоняться от формата, который вы упомянули, вам придется их настроить.

...