Как разобрать пронумерованную последовательность из списка имен файлов? - PullRequest
0 голосов
/ 24 декабря 2010

Я хотел бы автоматически разобрать a диапазон из пронумерованных последовательностей из уже отсортированных List<FileData> имен файлов, проверяя какие часть имени файла меняется.

Вот пример (расширение файла уже удалено):

Первое имя файла : IMG_0000
Последнее имя файла : IMG_1000
Пронумерованный диапазон, который мне нужен : 0000 и 1000

За исключением того, что мне нужно иметь дело с каждым возможным типом соглашения об именовании файлов, таким как:

0000 ... 9999
20080312_0000 ... 20080312_9999
IMG_0000 - Копировать ... IMG_9999 - Копировать
8er_green3_00001 .. 8er_green3_09999
и т.д.

  • Я бы хотел, чтобы весь диапазон с 0 дополнился, например 0001 не просто 1
  • Порядковый номер дополнен 0, например, 0001
  • Порядковый номер может быть расположен где угодно, например, IMG_0000 - Copy
  • Диапазон может начинаться и заканчиваться чем угодно, то есть не обязательно начинаться с 1 и заканчиваться 9999
  • Числа могут появляться несколько раз в имени файла последовательности, например 20080312_0000

Всякий раз, когда я получаю что-то, работающее на 8 случайных тестовых случаях, 9-й тест ломает все, и я в итоге перезапускаю с нуля.

В настоящее время я сравниваю только первого и последнего имен файлов (в отличие от перебора всех имен файлов):

void FindRange(List<FileData> files, out string startRange, out string endRange)
{
    string firstFile = files.First().ShortName;
    string lastFile = files.Last().ShortName;

    ...
}

У кого-нибудь есть какие-нибудь умные идеи? Возможно, что-то с Regex?

Ответы [ 4 ]

1 голос
/ 24 декабря 2010

Во-первых, я буду предполагать, что числа всегда дополняются нулями, так что они имеют одинаковую длину. Если нет, то впереди еще большие головные боли.

Во-вторых, предположим, что имена файлов точно одинаковы, за исключением компонента числа приращения.

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

var start = String.Empty;
var end = String.Empty;

for (var index = 0; index < firstFile.Length; index++)
{
    char c = firstFile[index];

    if (filenames.Any(filename => filename[index] != c))
    {            
        start += firstFile[index];
        end += lastFile[index];
    }
}    
// convert to int if required

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

1 голос
/ 24 декабря 2010

Используйте регулярное выражение для разбора чисел из имен файлов:

^.+\w(\d+)[^\d]*$

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

Сортировать эти строки в алфавитном порядке.Возьмите первое и последнее из этого отсортированного списка, чтобы получить минимальные и максимальные цифры.

1 голос
/ 24 декабря 2010

Если вы точно знаете, что файлы заканчиваются номером (например, _ \ d +) и сортируются, просто возьмите первый и последний элементы, и это ваш диапазон. Если имена файлов одинаковы, вы можете отсортировать список, чтобы получить их в числовом порядке. Если я не упустил что-то очевидное здесь - в чем проблема?

0 голосов
/ 24 декабря 2010

Вот мое решение. Он работает со всеми приведенными вами примерами и предполагает сортировку входного массива.

Обратите внимание, что он не выглядит исключительно для чисел; он ищет согласованную последовательность символов, которая может отличаться во всех строках. Поэтому, если вы укажете {"0000", "0001", "0002"}, он вернет «0» и «2» в качестве начальной и конечной строк, поскольку это единственная часть строк, которая отличается. Если вы дадите ему {"0000", "0010", "0100"}, он вернет вам «00» и «10».

Но если вы дадите ему {"0000", "0101"}, он будет скулить, поскольку различные части строки не являются смежными. Если вы хотите изменить это поведение, чтобы оно возвращало все от первого отличающегося символа до последнего, это нормально; Я могу сделать это изменение. Но если вы передаете ему тонну имен файлов, которые будут иметь последовательные изменения в области номеров, это не должно быть проблемой.

public static class RangeFinder
{
    public static void FindRange(IEnumerable<string> strings,
        out string startRange, out string endRange)
    {
        using (var e = strings.GetEnumerator()) {
            if (!e.MoveNext())
                throw new ArgumentException("strings", "No elements.");

            if (e.Current == null)
                throw new ArgumentException("strings",
                    "Null element encountered at index 0.");

            var template = e.Current;

            // If an element in here is true, it means that index differs.
            var matchMatrix = new bool[template.Length];

            int index = 1;

            string last = null;
            while (e.MoveNext()) {
                if (e.Current == null)
                    throw new ArgumentException("strings",
                        "Null element encountered at index " + index + ".");

                last = e.Current;
                if (last.Length != template.Length)
                    throw new ArgumentException("strings",
                        "Element at index " + index + " has incorrect length.");

                for (int i = 0; i < template.Length; i++)
                    if (last[i] != template[i])
                        matchMatrix[i] = true;
            }

            // Verify the matrix:
            // * There must be at least one true value.
            // * All true values must be consecutive.
            int start = -1;
            int end = -1;
            for (int i = 0; i < matchMatrix.Length; i++) {
                if (matchMatrix[i]) {
                    if (end != -1)
                        throw new ArgumentException("strings",
                            "Inconsistent match matrix; no usable pattern discovered.");

                    if (start == -1)
                        start = i;
                } else {
                    if (start != -1 && end == -1)
                        end = i;
                }
            }

            if (start == -1)
                throw new ArgumentException("strings",
                    "Strings did not vary; no usable pattern discovered.");

            if (end == -1)
                end = matchMatrix.Length;

            startRange = template.Substring(start, end - start);
            endRange = last.Substring(start, end - start);
        }
    }
}
...