Двухбайтовое сравнение строк в C # - PullRequest
4 голосов
/ 10 ноября 2010

У меня есть две строки, одна с двухбайтовым значением, а другая - одна байтовая. Результат сравнения строк возвращает false, как мне заставить их сравнивать корректно после игнорирования разницы в один / два байта?

string s1 = "smatsumoto11"
string s2 = "smatsumoto11"

В том же сценарии, если в SQL-сервере есть столбец nvarchar, содержащий значение smatsumoto11, запрос на выборку данных с условием where, содержащим строку smatsumoto11, вернет ту же строку. Мне нужна похожая семантика при сравнении строк в C #.

Я пробовал несколько вариантов, упомянутых в MSDN, но они, похоже, не работают.

Есть идеи?

Ответы [ 4 ]

6 голосов
/ 10 ноября 2010

Ваш s1 содержит так называемые символы "полной ширины", поэтому вы можете использовать string.Compare и сказать ему игнорировать ширину символов:

string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreWidth);

(Конечно, укажите другой CultureInfoпри необходимости.)

3 голосов
/ 10 ноября 2010

Перед выполнением сравнения вы можете попробовать " Нормализовать " ваших строк:

Возвращает новую строку, текстовое значение которой совпадает с этой строкой, но двоичное представление которой находится в указанной форме нормализации Unicode.

Некоторые символы Unicode имеют несколько эквивалентных двоичных представлений, состоящих из наборов комбинируемых и / или составных символов Unicode. Наличие нескольких представлений для одного символа усложняет поиск, сортировку, сопоставление и другие операции.

1 голос
/ 29 октября 2015

Несмотря на то, что принятый ответ работает и является верным в отношении того, что основная проблема заключается в «широких» символах, в Вопросе есть несколько неправильных представлений и технических аспектов, которые следует рассмотреть, чтобы лучшепонимание того, что на самом деле происходит здесь, как в .NET, так и в SQL Server.

Первый:

У меня есть две строки, одна с двойнымзначение байта, а другой - один байт-один.

Нет, вы не делаете.У вас есть две строки Unicode, закодированные как UTF-16 Little Endian (так работают все Windows и .NET).И хотя с практической точки зрения, в большинстве случаев символы являются двухбайтовыми, они охватывают только 62 000 - 63 000 (или около того) символов (т. Е. Кодовые точки между U + 0000 и U + FFFF - или 0 - 65 535), чтоявляются "действительными" символами).Но Unicode позволяет отображать чуть более 1,1 миллиона кодовых точек, и в настоящее время уже более 260 000 из этих кодовых точек уже сопоставлены .Точки кода выше U + FFFF / 65 535, известные как дополнительные символы, отображаются на наборы из двух двухбайтовых значений, известных как суррогатные пары.Поэтому, хотя они используются реже, большинство кодовых точек Unicode на самом деле составляют 4 байта.

Секунда:

Результат сравнения строк возвращает false, какя могу заставить их сравнивать правильно

Буквы в s1 = "smatsumoto11" известны как символы "Fullwidth".Вы можете увидеть их полный список здесь:

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:East_Asian_Width=Fullwidth:]

Некоторое объяснение, почему в первую очередь существуют разные ширины, можно найти здесь:

http://unicode -table.com / ru / blocks / halfwidth-and-fullwidth-forms /

Если вы хотите сравнить две строки в вопросе так, чтобы они были равны, вы можетелибо используйте метод String.Compare (String, String, CultureInfo, CompareOptions) , как указано в ответе @ Arnout, либо используйте CompareInfo.Compare (String, String, CompareOptions) какследует:

CompareInfo.Compare(s1, s2, CompareOptions.IgnoreWidth)

Третье:

В том же сценарии, если у вас есть столбец nvarchar в SQL-сервере, который содержит значение smatsumoto11, запросдля извлечения данных с условием where, содержащим строку smatsumoto11, будет возвращаться та же строка.

Это потенциально опасный способ думать о сравнении строк.Нет особого способа сравнения строк практически в любой базе данных, если только строки не находятся в 7-битном ASCII (значения 0–127), который даже не включает кодовые страницы, и я не знаю, является ли это вообще опцией,Сравнения основаны на конкретной LCID / Locale / Culture / Collation.Параметры сортировки по умолчанию в SQL Server (по крайней мере, в США): SQL_Latin1_General_CP1_CI_AS, что не учитывает регистр и не учитывает акцент.Он также использует кодовую страницу 1252 (которая влияет на данные CHAR / VARCHAR, а не NCHAR / NVARCHAR) и культуру "en-US".Параметры сортировки для других культур / LCID могут не совпадать с полной шириной и «полушириной».Кроме того, параметры сортировки, в имени которых указано _WS, определенно не будут приравнивать эти две строки, поскольку _WS означает "чувствительный к ширине", который является значением по умолчанию для сравнения .NET, если вы не указали параметр CompareOptions.IgnoreWidth.

Если вы выполните следующий запрос, чтобы найти сопоставления с _WS в названии, вы обнаружите, что из общего числа сопоставлений 1776 из 1785 сопоставлений, которые чувствительны к ширине, не соответствовать этим двум строкам (по крайней мере, в SQL Server 2012).Конечно, есть также 262 двоичных сопоставления (т. Е. Имена, заканчивающиеся либо устаревшими _BIN, либо предпочтительными _BIN2), которые также не будут приравнивать эти строки, но это не является проблемой чувствительности к ширине.

SELECT *
FROM sys.fn_helpcollations()
WHERE [name] LIKE N'%[_]WS%'
ORDER BY [name];
-- 1776 out of 3885 on SQL Server 2012

Кроме того, как я только что упомянулd, неудачное (и устарело) сопоставление по умолчанию SQL_Latin1_General_CP1_CI_AS или даже лучшая версия Latin1_General_100_CI_AS не чувствительна к регистру. Таким образом, все сравниваемые строки являются строчными, поэтому они равны при использовании только CompareOptions.IgnoreWidth, но если вы хотите эмулировать эти конкретные параметры сортировки в SQL Server, поведение по умолчанию .NET с учетом регистра не будет соответствовать поведение SQL Server. Чтобы лучше соответствовать поведению SQL Server (по крайней мере, для тех сопоставлений или любых помеченных как имеющие _CI и , а не имеющих _WS, вам также необходимо включить параметр CompareOptions.IgnoreCase следующим образом: 1075 *

CompareInfo.Compare(s1, s2, CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase)

// or

String.Compare(s1, s2, CultureInfo.CurrentCulture, 
               CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase)

Дополнительные ресурсы:

Сравнение строк в .NET Framework

Рекомендации по использованию строк в .NET Framework

1 голос
/ 10 ноября 2010

Моя машина говорит, что s1 в MS Mincho.

MS Mincho (MS 明朝) - распространяется с японской версией Windows 3.1 или более поздней, некоторые версии Internet Explorer 3, пакет японских шрифтов, все регионы в Windows XP, Microsoft Office v.X до 2004 г.

Следующий ответ полностью устарел от ответа Arnout .

Я знаю трюк , который работает как //TRANSLIT в iconv и, похоже, работает здесь.

        string s1 = "smatsumoto11";
        string s2 = "smatsumoto11";

        string conv = Encoding.ASCII.GetString(Encoding.GetEncoding("Cyrillic").GetBytes(s1));

        if (conv == s2) Console.WriteLine("They are the same!");

Однажды мне действительно нужно попытаться выяснить, как это работает ...

...