【问题标题】:How to parse a numbered sequence from a List of filenames?如何从文件名列表中解析编号序列?
【发布时间】:2010-12-24 00:06:09
【问题描述】:

我想自动解析 已经排序List<FileData>编号序列范围文件名通过检查文件名的哪一部分更改。

这是一个示例(文件扩展名已被删除):

第一个文件名IMG_0000
最后一个文件名IMG_1000
我需要的编号范围00001000

除了我需要处理每一种可能的文件命名约定类型,例如:

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;

    ...
}

有人有什么聪明的主意吗?也许与正则表达式有关?

【问题讨论】:

  • 我不太确定问题是什么。什么是输入,什么是预期输出?你能发布一些你的测试用例吗?

标签: c# parsing c#-3.0 filenames


【解决方案1】:

如果您保证知道文件以数字结尾(例如 _\d+)并已排序,则只需抓住第一个和最后一个元素,这就是您的范围。如果文件名都相同,您可以对列表进行排序以按数字顺序排列它们。除非我在这里遗漏了一些明显的东西——问题出在哪里?

【讨论】:

  • 并不总是在最后,参见示例:IMG_0000 - Copy ... IMG_9999 - Copy
  • 是的,它们已排序,我已经有了第一个和最后一个元素。正如柯克所说,这个数字可以随机定位。我只想将范围开始/结束数字(例如“0001”和“9999”)作为子字符串,因为目前我正在使用 6 个函数来定位文本 surrounding 数字而且它都失控了.
  • 如果它们的数字位于相同的位置并且用零填充,它们仍然应该排序。只需使用正则表达式将它们拉出:\d+
  • 这就是问题的一半——确定他们的位置。
  • 你为什么关心他们的职位?只需在 \d+ 上匹配,您将获得所有数字。如果您知道长度,请使用它,即 \d{5} 表示五位数字。
【解决方案2】:

使用正则表达式从文件名中解析出数字:

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

从这些已解析的字符串中,找出最大长度,并将小于最大长度的任何内容用零填充。

按字母顺序对这些填充字符串进行排序。从这个排序列表中取出第一个和最后一个,为您提供最小和最大数字。

【讨论】:

  • 这仅在数字出现在文件名末尾时才有效。如果文件名中有多个数字前面有下划线,你可以在其中放一个 * 会让人感到困惑。
  • 我认为 OP 需要查看文件名的 更改 组件以确定正确的部分,而不是仅仅查找数字部分。
  • @Kirk - 我编辑了正则表达式以匹配最后一组数字,无论它们是否出现在字符串的末尾。用简单的英语,它说“匹配任何东西,后跟一个单词字符,然后是一组数字,后面可能跟也可能不跟任何不是数字的东西”。关于您的第二条评论,如果是这种情况,我不知道该怎么做!似乎解决方案是不确定的。
【解决方案3】:

首先,我假设数字总是用零填充,因此它们的长度相同。如果不是,那么更令人头疼的问题还在后面。

其次,假设文件名完全相同除了增量编号部分之外。

如果这些假设成立,那么算法应该是查看第一个和最后一个文件名中的每个字符,以确定哪些相同位置的字符不匹配。

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:更改为检查每个文件名,直到发现差异。效率不高,但非常简单明了。

【讨论】:

  • 是的,我假设它们至少是零填充的。不错的尝试,但如果名字是“0000”而最后一个名字是“0070”则不起作用,因为最后一个“0”会匹配。
  • 啊当然。那么你有多少个文件名,你需要多快来处理它们?最简单的解决方案是检查每个文件名......我将编辑我的答案。
  • 介于 2 到 500k 之间,取决于项目。时间并不真的很重要,尽管扫描 500k 字符串应该不会花费很长时间?
  • Any() 操作符在满足后立即停止。如果您想加快速度,请添加更多条件 - 例如如果您确定只需要查看数字,则添加if (!Char.IsNumber(c)) continue; 以移动到下一个字符。
  • 字符串操作真的很快;如果在实时系统中不需要它,那么它会很好,但如果它被集成到其他东西中,你需要对其进行测试。 500k 个字符串并不多。
【解决方案4】:

这是我的解决方案。它适用于您提供的所有示例,并且它假定输入数组已排序。

请注意,它并非专门针对数字;它查找可能在所有字符串中不同的一致字符序列。因此,如果您向它提供{"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);
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-03-30
    • 2020-10-31
    • 2019-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 2021-04-08
    • 1970-01-01
    相关资源
    最近更新 更多