Проблема с суррогатными символами юникода в F #
Я работаю со строками, которые могут содержать суррогатные символы Юникода (не BMP, 4 байта на символ).
Когда я использую формат "\ Uxxxxxxxxv", чтобы указать суррогатный символ в F # - для некоторых символов он дает отличный результат, чем в случае С#. Например:
С#
string s = "\U0001D11E";
bool c = Char.IsSurrogate(s, 0);
Console.WriteLine(String.Format("Length: {0}, is surrogate: {1}", s.Length, c));
Дает: Length: 2, is surrogate: True
F #:
let s = "\U0001D11E"
let c = Char.IsSurrogate(s, 0)
printf "Length: %d, is surrogate: %b" s.Length c
Дает: Length: 2, is surrogate: false
Примечание. Некоторые суррогатные символы работают в F # ( "\ U0010011", "\ U00100011" ), но некоторые из них не работают.
В: Это ошибка в F #? Как я могу обрабатывать разрешенные суррогатные символы Юникода в строках с помощью F # (имеет ли F # другой формат или только способ использовать Char.ConvertFromUtf32 0x1D11E
)
Update:
s.ToCharArray()
дает для F # [| 0xD800; 0xDF41 |]
; для С# { 0xD834, 0xDD1E }
Ответы
Ответ 1
Это, очевидно, означает, что F # делает ошибку при анализе некоторых строковых литералов. Это подтверждается тем фактом, что вы упомянули не BMP, а в UTF-16 он должен быть представлен как пара суррогатов.
Суррогаты - это слова в диапазоне 0xD800-0xDFFF, в то время как ни один из символов в построенной строке не подходит в этом диапазоне.
Но обработка суррогатов не изменяется, поскольку структура (что находится под капотом) одинакова. Итак, у вас уже есть ответ в вашем вопросе - если вам нужны строковые литералы с символами не BMP в вашем коде, вы должны просто использовать Char.ConvertFromUtf32
вместо \UXXXXXXXX нотации. И вся остальная обработка будет такой же, как всегда.
Ответ 2
Это известная ошибка в компиляторе F #, поставляемом с VS2010 (и SP1); исправление появляется в битах VS11, поэтому, если у вас есть бета-версия VS11 и вы используете компилятор F # 3.0, вы увидите, что это ведет себя так, как ожидалось.
(Если другие ответы/комментарии здесь не дают вам подходящего обходного пути, дайте мне знать.)
Ответ 3
Мне кажется, что это связано с различными формами нормализации.
И в С#, и в F # s.IsNormalized() возвращает true
Но в С#
s.ToCharArray() дает нам {55348, 56606}//0xD834, 0xDD1E
и в F #
s.ToCharArray() дает нам {65533, 57422}//0xFFFD, 0xE04E
И как вы, вероятно, знаете System.Char.IsSurrogate реализуется следующим образом:
public static bool IsSurrogate(char c)
{
return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END);
}
где
HIGH_SURROGATE_START = 0x00d800;
LOW_SURROGATE_END = 0x00dfff;
Итак, в С# сначала char (55348) меньше LOW_SURROGATE_END, но в F # сначала char (65533) не меньше LOW_SURROGATE_END.
Надеюсь, это поможет.