使用CultureInfo 1031和IndexOf时出现ArgumentOutOfRangeException错误

10
string s = "Gewerbegebiet Waldstraße"; //other possible input "Waldstrasse"

int iFoundStart = s.IndexOf("strasse", StringComparison.CurrentCulture);
if (iFoundStart > -1)
    s = s.Remove(iFoundStart, 7);

我正在运行CultureInfo 1031(德语)。

IndexOf匹配定义的'strasse'中的'straße'或'strasse'并返回18作为位置。

Remove和Replace都没有重载以设置文化。

如果我使用Remove删除6个字符,如果输入字符串是'strasse',则会剩下一个字符,而'straße'将正常工作。 如果输入字符串是'straße',我删除7个字符,就会出现ArgumentOutOfRangeException。

有没有一种安全地删除找到的字符串的方法? 是否有任何提供IndexOf的最后一个索引的方法? 我更深入地了解了IndexOf及其底层的本机代码 - 所以无法做任何自己的事情...


@dotctor 我认为OP的意思是string.Replace不考虑文化差异,所以"ss"不能匹配"ß"。 - juharr
我正在运行在 en-US 并遇到了这个问题。问题是 IndexOf 的行为不同。 - M.kazem Akhgary
2
如果您首先执行了s.Replace("ß", "ss")会怎样? - juharr
@JakubLortz,它们不一样。但是,即使我使用indexOf时得到了正确的索引。如果我使用StringComparison.Ordinal,那么当然会给我-1,但是.Net开发人员应该考虑为ReplaceRemove方法添加这些重载,并且它们应该像IndexOf一样运行。 - M.kazem Akhgary
生命太短暂了,学习德语是不值得的! - Hamid Pourjam
显示剩余3条评论
1个回答

5

本机Win32 API确实公开了找到的字符串长度。您可以使用P/Invoke直接调用FindNLSStringEx

static class CompareInfoExtensions
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    private static extern int FindNLSStringEx(string lpLocaleName, uint dwFindNLSStringFlags, string lpStringSource, int cchSource, string lpStringValue, int cchValue, out int pcchFound, IntPtr lpVersionInformation, IntPtr lpReserved, int sortHandle);

    const uint FIND_FROMSTART = 0x00400000;

    public static int IndexOfEx(this CompareInfo compareInfo, string source, string value, int startIndex, int count, CompareOptions options, out int length)
    {
        // Argument validation omitted for brevity
        return FindNLSStringEx(compareInfo.Name, FIND_FROMSTART, source, source.Length, value, value.Length, out length, IntPtr.Zero, IntPtr.Zero, 0);
    }
}

static class Program
{
    static void Main()
    {
        var s = "<<Gewerbegebiet Waldstraße>>";
        //var s = "<<Gewerbegebiet Waldstrasse>>";
        int length;
        int start = new CultureInfo("de-DE").CompareInfo.IndexOfEx(s, "strasse", 0, s.Length, CompareOptions.None, out length);
        Console.WriteLine(s.Substring(0, start) + s.Substring(start + length));
    }
}

我没有看到完全使用BCL来实现这一点的方法。


如果我想匹配'Berliner Straße'并使用CompareOptions.IgnoreCase,这将失败 - 你有什么想法为什么? - isHuman
@isHuman 我省略了从CompareOptionsFindNLSStringEx选项值的转换:你可以看到 options 参数没有被使用。你需要添加一个从 CompareOptions.IgnoreCaseLINGUISTIC_IGNORECASENORM_IGNORECASE 的转换(由你决定)。 - user743382
这似乎有效。我定义了另一个标志 LINGUISTIC_IGNORECASE 并将其与 FIND_FROMSTART 结合使用 |。谢谢,可惜没有更高级的方法来做到这一点。 - isHuman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接