someString.IndexOf (someString) возвращает 1 вместо 0 under.NET 4


мы недавно обновили все наши проекты с .NET 3.5 до .NET 4. Я столкнулся с довольно странной проблемой в отношении string.IndexOf().

мой код, очевидно, делает что-то немного другое, но в процессе исследования проблемы, я обнаружил, что вызов IndexOf() на строку с собой возвращается 1 вместо 0. Другими словами:

string text = "xADx2D";          // problem happens with "­-dely N.China", too;
int index = text.IndexOf(text);    // see update note below.

дал мне индекс 1, а не 0. Несколько вещей, чтобы отметить об этом проблема:

  • проблемы, похоже, связаны с этими дефисами (первый символ-мягкий дефис Unicode, второй-обычный дефис).

  • Я дважды проверил, это не происходит в .NET 3.5, но делает в .NET 4.

  • изменение IndexOf() для выполнения порядкового сравнения исправляет проблему, поэтому по какой-то причине первый символ игнорируется по умолчанию IndexOf.

тут кто-нибудь знает, почему это происходит?

EDIT

Извините, ребята, сделал немного материала на оригинальном посту и получил скрытый тире там дважды. Я обновил строку, это должно возвращать индекс 1 вместо 2, пока вы вставляете его в правильный редактор.

обновление:

изменил исходную строку задачи на ту, где каждый фактический символ четко виден (используя экранирование). Это упрощает вопрос немного.

3 56

3 ответа:

ваша строка состоит из двух символов: a мягкий перенос (кодовая точка Unicode 173) и тире (кодовая точка Unicode 45).

Wiki: согласно стандарту Unicode, мягкий дефис не отображается, если линия не нарушена в этой точке.

при использовании "\xAD\x2D".IndexOf("\xAD\x2D") в .NET 4, похоже, игнорируется, что вы ищете мягкий дефис, возвращая начальный индекс 1 (индекс \x2D). В .NET 3.5 это возвращает 0.

больше удовольствия, если вы запустите этот код (так когда только ищу мягкий дефис):

string text = "\xAD\x2D";
string shy = "\xAD";
int i1 = text.IndexOf(shy);

затем i1 становится 0, независимо от используемой версии .NET. Результат text.IndexOf(text); действительно меняется, что на первый взгляд выглядит как ошибка для меня.

насколько я могу отследить через фреймворк, более старые версии .NET используют InternalCall до IndexOfString() (Я не могу понять, к какому API вызов, который идет), в то время как из .NET 4 A QCall до InternalFindNLSStringEx() сделано, что в свою очередь вызывает FindNLSStringEx().

проблема (я действительно не могу понять, если это намеренное поведение) действительно происходит при вызове FindNLSStringEx:

LPCWSTR lpStringSource = L"\xAD\x2D";
LPCWSTR lpStringValue = L"\xAD";

int length;

int i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringValue,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringSource,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

Console::ReadLine();

выводит 0, а затем 1. Обратите внимание, что length, параметр out, указывающий длину найденной строки, равен 0 после первого вызова и 1 после второго; мягкий дефис считается имеющим длину 0.

обходной путь заключается в использовании text.IndexOf(text, StringComparison.OrdinalIgnoreCase);, как вы отметили. Это делает QCall в InternalCompareStringOrdinalIgnoreCase(), который, в свою очередь, называет FindStringOrdinal(), которая возвращает 0 для обоих случаев.

похоже, это ошибка в. NET4, и новые изменения вернулись в . NET4 Beta1 к предыдущей версии такой же как .NET 2.0/3.0/3.5.

что нового в BCL в .NET 4.0 CTP (блоги MSDN):

изменения Безопасности строк в .NET 4

частичные совпадающие перегрузки по умолчанию в системе.Строка (StartsWith, EndsWith, IndexOf и LastIndexOf) имеет было изменено, чтобы быть агностиком культуры (порядковый номер) по умолчанию.

это изменение повлияло на поведение String.IndexOf метод путем изменения их для выполнения порядкового (байт-для-байт) сравнения по умолчанию будет изменен на использование CultureInfo.InvariantCulture вместо CultureInfo.CurrentCulture.

обновление для .NET 4 Beta 1

чтобы поддерживать высокую совместимость между .NET 4 и предыдущими выпусками, мы решили отменить это изменение. Этот поведение частичных совпадающих перегрузок String по умолчанию и методов String и Char ToUpper и ToLower теперь ведут себя так же, как и в .NET 2.0/3.0/3.5. Изменение обратно к исходному поведению присутствует в .NET 4 Beta 1.


исправить измените метод сравнения строк на перегрузку, которая принимает System.StringComparison перечисление в качестве параметра, и указать Ordinal или OrdinalIgnoreCase.

// string contains 'unicode dash' \x2D
string text = "\xAD\x2D"; 

// woks in .NET 2.0/3.0/3.5 and .NET 4 Beta 1 and later
// but seems be buggy in .NET 4 because of 'culture-sensitive' comparison        
int index = text.IndexOf(text); 

// fixed version
index = text.IndexOf(text, StringComparison.Ordinal); 

С документация (Курсив мой):

этот метод выполняет слово (чувствительный к регистру и культуре) поиск с использованием текущего языка и региональных параметров.

Ie. некоторые отдельные кодовые точки будут рассматриваться как равные.

что произойдет, если вы используете перегрузку, которая принимает StringComparison значение и передают StringComparison.Ordinal чтобы избежать культурных зависимостей?