Проблема с суррогатными символами юникода в 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.

Надеюсь, это поможет.