代码之家  ›  专栏  ›  技术社区  ›  Lasse V. Karlsen

写一篇比我更自然的文章

  •  8
  • Lasse V. Karlsen  · 技术社区  · 14 年前

    我在这里添加了这个问题的答案: Sorting List<String> in C# 它需要一个自然的排序顺序,处理嵌入的数字。

    然而,我的实现是幼稚的,取代了所有关于应用程序如何不能通过假设正确处理Unicode的文章(Turkey测试任何人?),我想我应该请求帮助编写更好的实现。或者,如果有内置的.NET方法,请告诉我:)

    我对那个问题的答案的实现只是通过字符串,逐个字符比较,直到它在两个字符串中都遇到一个数字。然后,它从两个字符串中提取连续的数字,这会导致长度不同,用前导零填充最短的数字,然后进行比较。

    但是,它有问题。

    例如,如果在字符串x中有两个共同构成字符的代码点,但在另一个字符串中只有一个代码点,即该字符。

    我的算法在这些字符上会失败,因为它将音调符号代码点视为单个字符,并将其与另一个字符串中的_进行比较。

    有人能指导我如何正确处理这件事吗?我希望支持指定 CultureInfo 对象处理语言问题,例如比较德国的“ss”和“_159;”,以及类似的问题。

    我想我需要让我的代码枚举“真实字符”(我不知道这里的真实术语)而不是单个代码点。

    正确的方法是什么?

    另外,如果“自然”的意思是“人类期望它工作的方式”,我会加上以下几点思考:

    • 日期和时间呢?
    • 浮点值呢?
    • 还有其他被认为是“自然”的序列吗?
      • 这应该伸展多远?(伊尼、米尼、米尼、莫伊)
    2 回复  |  直到 14 年前
        1
  •  7
  •   Hans Passant    14 年前

    这在Windows中已经可用,shell在资源管理器窗口中排列文件时使用自然排序顺序。它使用的比较函数被导出并可用于任何程序,至少从Windows2000开始。虽然P/Invoke不是最好的解决方案,但它确实具有在过去10多年中测试了数十亿次的巨大优势。以用户已经熟悉的方式对字符串进行排序。

    处理音调符号已经是.NET的一部分,string.normalize()方法负责处理它。

    下面是一个使用它的示例程序,它按照原始线程中的请求对字符串进行正确排序:

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    class Program {
        static void Main(string[] args) {
            string[] arr = new string[] { "1", "5", "3", "6", "11", "9", "NUM1", "NUM0" };
            Array.Sort(arr, new LogicalComparer());
            foreach (string s in arr) Console.WriteLine(s);
            Console.ReadLine();
        }
    }
    class LogicalComparer : IComparer<string> {
        public int Compare(string x, string y) {
            return StrCmpLogicalW(x.Normalize(), y.Normalize());
        }
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int StrCmpLogicalW(string s1, string s2);
    }
    
        2
  •  2
  •   Jonas Wagner    14 年前

    我对.NET不太了解,但由于它也是一个算法问题,下面是我的两分钱:

    我会尝试将字符串拆分为标记,可能使用正则表达式。然后,您可以使用适当的比较函数(取决于令牌的类型),逐个令牌比较字符串令牌。

    更具体地说:

    1. 为日期、数字、单词等定义正则表达式…最后一个应该是与任何字符匹配的回退表达式。
    2. 首先尝试每个表达式(最具体),直到其中一个表达式在两个字符串的开头匹配
    3. 提取匹配的部分,并使用适当的比较函数对其进行比较。
    4. 如果相等,请从两个字符串的开头删除匹配项,然后从步骤2重复。

    使用正则表达式,如果不使用 [a-zA-Z] 但是适当的角色类 [:alpha:] .

    对于_的不同形式的比较,您可以尝试 normalize 首先是字符串。