【问题标题】:Length of substring matched by culture-sensitive String.IndexOf method与区域性敏感的 String.IndexOf 方法匹配的子字符串的长度
【发布时间】:2013-12-27 02:48:09
【问题描述】:

我尝试编写一种文化感知字符串替换方法:

public static string Replace(string text, string oldValue, string newValue)
{
    int index = text.IndexOf(oldValue, StringComparison.CurrentCulture);
    return index >= 0
        ? text.Substring(0, index) + newValue + text.Substring(index + oldValue.Length)
        : text;
}

但是,它在 Unicode 组合字符上卡住了:

// \u0301 is Combining Acute Accent
Console.WriteLine(Replace("déf", "é", "o"));       // 1. CORRECT: dof
Console.WriteLine(Replace("déf", "e\u0301", "o")); // 2. INCORRECT: do
Console.WriteLine(Replace("de\u0301f", "é", "o")); // 3. INCORRECT: dóf

要修复我的代码,我需要知道在第二个示例中,String.IndexOf 仅匹配一个字符 (é),即使它搜索了两个 (e\u0301)。同样,我需要知道在第三个示例中,String.IndexOf 匹配了两个字符(e\u0301),即使它只搜索了一个字符(é)。

如何确定String.IndexOf匹配的子串的实际长度?

注意:textoldValue 执行 Unicode 规范化(如 James Keesey 所建议)将适应组合字符,但连字仍然是一个问题:

Console.WriteLine(Replace("œf", "œ", "i"));  // 4. CORRECT: if
Console.WriteLine(Replace("œf", "oe", "i")); // 5. INCORRECT: i
Console.WriteLine(Replace("oef", "œ", "i")); // 6. INCORRECT: ief

【问题讨论】:

    标签: c# .net unicode substring


    【解决方案1】:

    使用以下方法适用于您的示例。它通过比较值来工作,直到找到源字符串中需要多少个字符才能等于oldValue,然后使用它而不是简单地使用oldValue.Length

    public static string Replace(string text, string oldValue, string newValue)
    {
        int index = text.IndexOf(oldValue, StringComparison.CurrentCulture);
        if (index >= 0)
            return text.Substring(0, index) + newValue +
                     text.Substring(index + LengthInString(text, oldValue, index));
        else
            return text;
    }
    static int LengthInString(string text, string oldValue, int index)
    {
        for (int length = 1; length <= text.Length - index; length++)
            if (string.Equals(text.Substring(index, length), oldValue,
                                                StringComparison.CurrentCulture))
                return length;
        throw new Exception("Oops!");
    }
    

    【讨论】:

    • 我担心我不得不做这样的事情。在内部,String.IndexOf 必须知道它匹配了多少个字符,但我很惊讶没有(明显的?)方法来获取这些信息。
    • LengthInString 循环应该向下计数。匹配可以比oldValue。我们可能应该获得最长的匹配。
    • for (int length = 1; length &lt; text.Length - index; length++) 应该是 for (int length = 1; length &lt;= text.Length - index; length++) - 例如。如果 text.Length 是 3 并且 index 是 2 循环什么也不做,但它应该迭代一次。
    • @JimW 谢谢!我没有意识到这一点。我已经在我的回答中解决了这个问题。一个接一个的错误是我的克星..
    【解决方案2】:

    您需要自己直接致电FindNLSStringFindNLSStringExString.IndexOf 使用 FindNLSStringEx,但您需要的所有信息都可以在 FindNLSString 中找到。

    这是一个示例,说明如何重写适用于您的测试用例的 Replace 方法。请注意,如果您想使用系统语言环境或提供您自己的语言环境,我正在使用当前用户语言环境阅读 API 文档。我还为标志传递了 0,这意味着它将使用语言环境的默认字符串比较选项,同样,文档可以帮助您提供不同的选项。

    public const int LOCALE_USER_DEFAULT = 0x0400;
    
    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    internal static extern int FindNLSString(int locale, uint flags, [MarshalAs(UnmanagedType.LPWStr)] string sourceString, int sourceCount, [MarshalAs(UnmanagedType.LPWStr)] string findString, int findCount, out int found);
    
    public static string ReplaceWithCombiningCharSupport(string text, string oldValue, string newValue)
    {
        int foundLength;
        int index = FindNLSString(LOCALE_USER_DEFAULT, 0, text, text.Length, oldValue, oldValue.Length, out foundLength);
        return index >= 0 ? text.Substring(0, index) + newValue + text.Substring(index + foundLength) : text;
    }
    

    【讨论】:

      【解决方案3】:

      我说得太早了(以前从未见过这种方法),但还有另一种选择。您可以使用StringInfo.ParseCombiningCharacters() 方法获取每个实际字符的开头,并使用它来确定要替换的字符串的长度。


      在调用 Index 之前,您需要对这两个字符串进行规范化。这将确保源字符串和目标字符串的长度相同。

      请参阅描述此确切问题的 String.Normalize() 参考页面。

      【讨论】:

      • 我不想规范化不被替换的字符。
      • @MichaelLiu 为什么不呢?它代表相同的文本。
      • @TimS.:如果用户有意识地选择以特定方式输入原始文本,我宁愿不要搞乱那个选择。
      • 如果您阅读 Normalize 的文档,您会发现没有其他方法(在库中)可以做到这一点。如果您真的不想规范化字符串,那么您将不得不自己动手。找到字符串的开头,然后匹配允许组合字符的每个字符,直到找出真正的长度。
      • 事实证明归一化是不够的。例如,它不处理连字:OE = Œ
      猜你喜欢
      • 1970-01-01
      • 2019-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-24
      • 2022-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多