【问题标题】:Canonicalize string using CultureInfo and CompareOptions使用 CultureInfo 和 CompareOptions 规范化字符串
【发布时间】:2013-06-13 08:09:59
【问题描述】:

目前我有代码,它基于 CultureInfo cultureInfo = new CultureInfo("ja-JP") 使用

进行搜索
bool found = cultureInfo.CompareInfo.IndexOf(x, y,
    CompareOptions.IgnoreCase | 
    CompareOptions.IgnoreKanaType | 
    CompareOptions.IgnoreWidth
) >= 0;

由于x.IndexOf(y) 更快,而我的xes 很多而且很少更改,我想规范化xes 一次,并在执行搜索时做一个简单的

canonicalizedX.indexOf(canonicalize(y));

我的问题:.net 库中是否有任何东西可以使用我的CultureInfoCompareOptions 来实现canonicalize() 函数?

【问题讨论】:

    标签: .net globalization


    【解决方案1】:

    您基本上是在问:“.NET 是否给了我一种将片假名映射到平假名和全宽到半宽的方法,以便我可以进行快速比较?”答案是肯定的。您必须自己实施。

    这是相当困难的。 .NET 中的字符串比较由相当广泛的字符比较表驱动。然而,它们针对比较而不是字符替换进行了优化。通过查看源代码,您可以了解 CLR 执行此操作的方式。下载 SSCLI20 发行版并查看 clr\src\classlibnative\nls\sortingtable.cpp 源代码文件。 NativeCompareInfo::LongCompareStringW() 函数进行比较,您会看到它使用 COMPARE_OPTIONS_IGNOREKANATYPE 和 COMPARE_OPTIONS_IGNOREWIDTH 标志。请注意它如何使用假名的特殊规则,走“慢路”。这个函数是大量的,你可以从这个反向工程替换算法的可能性低到零,可以很快放弃它。日语正字法是复杂的

    如果你比较的字符串是稳定的,那么考虑存储比较结果并重新使用它。

    【讨论】:

    • 日语正字法可能是最复杂的,但规范化,在这种情况下,将 Utf-16 字符串转换为小写、平假名、全角字符非常简单,如果你对语言和 Unicode 有一点了解的话少量。它基本上就像toLowerCase,除了不是一个单一的“代码点范围+偏移”组合,每个平假名、片假名、半角片假名、全角拉丁文都有一个。不过,我想重用任何现有的轮子。 CompareInfo.GetSortKey docs btw 将我带到LCMapString
    • Re:“重用比较结果”。我的“干草堆”相当稳定,但“针头”却不是。所以我在搜索时实际做的是在 suffix array 中进行简单的查找,我为大海捞针中的每个字符串创建一次。结果证明比String.IndexOf 快得多,对于树结构索引来说并不奇怪。
    【解决方案2】:

    我最终使用了LCMapStringEx,它对我来说很好用。它不是基于(任意组)CompareOptions,而是CompareInfo.GetSortKey docs 引导我到LCMapString,所以我的indexOf 的规范化字符串的效果应该与CultureInfo.CompareInfo.IndexOf 产生相同的结果,使用硬编码的CompareOptions,这里称为dwMapFlags

    public static string Canonicalize(string src)
    {
        string localeName = "ja-JP";
        string nResult = src;
    
        int nLen, nSize;
    
        uint dwMapFlags = LCMAP_LOWERCASE | LCMAP_HIRAGANA | LCMAP_FULLWIDTH;
        IntPtr ptr, pZero = IntPtr.Zero;
    
        nLen = src.Length;
        nSize = LCMapStringEx(localeName, dwMapFlags, src, nLen, IntPtr.Zero, 0, pZero, pZero, pZero);
        if (nSize > 0)
        {
            nSize = nSize * sizeof(char);
            ptr = Marshal.AllocHGlobal(nSize);
            try
            {
                nSize = LCMapStringEx(localeName, dwMapFlags, src, nLen, ptr, nSize, pZero, pZero, pZero);
                if (nSize > 0) nResult = Marshal.PtrToStringUni(ptr, nSize);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
        }
    
        return nResult;
    }
    
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern int LCMapStringEx(
         string lpLocaleName,
         uint dwMapFlags,
         string lpSrcStr,
         int cchSrc,
         [Out]
         IntPtr lpDestStr,
         int cchDest,
         IntPtr lpVersionInformation,
         IntPtr lpReserved,
         IntPtr sortHandle);
    
    private const uint LCMAP_LOWERCASE = 0x100;
    private const uint LCMAP_UPPERCASE = 0x200;
    private const uint LCMAP_SORTKEY = 0x400;
    private const uint LCMAP_BYTEREV = 0x800;
    private const uint LCMAP_HIRAGANA = 0x100000;
    private const uint LCMAP_KATAKANA = 0x200000;
    private const uint LCMAP_HALFWIDTH = 0x400000;
    private const uint LCMAP_FULLWIDTH = 0x800000;
    

    我也尝试了Microsoft.VisualBasic.StrConv,它有效,但速度是 pinvoking LCMapStringEx 的两倍。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-10
      • 2018-09-29
      • 2020-02-12
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 2013-06-29
      • 2011-05-08
      相关资源
      最近更新 更多