Ошибка обнаружения суррогатной пары

Я работаю над второстепенным побочным проектом на F#, который включает перенос существующего кода C# на F#, и я, похоже, столкнулся с разницей в том, как обрабатываются регулярные выражения между двумя языками (я публикую это, чтобы, надеюсь, узнать, что я просто сделал что-то не так).

Эта второстепенная функция просто обнаруживает суррогатные пары, используя трюк с регулярными выражениями, описанный здесь. Вот текущая реализация:

let isSurrogatePair input =
    Regex.IsMatch(input, "[\uD800-\uDBFF][\uDC00-\uDFFF]")

Если я затем выполню его против известной суррогатной пары, например:

let result = isSurrogatePair "????野????"
printfn "%b" result

Я получаю false в окне FSI.

Если я использую эквивалент С#:

public bool IsSurrogatePair(string input)
{
    return Regex.IsMatch(input, "[\uD800-\uDBFF][\uDC00-\uDFFF]");
}

И то же входное значение, я (правильно) получаю true обратно.

Это реальная проблема? Я просто делаю что-то неправильно в своей реализации F#?


person Sven Grosen    schedule 31.03.2015    source источник


Ответы (2)


Похоже, существует ошибка в том, как F# кодирует экранированные символы Unicode.
Вот из F# Interactive (обратите внимание на два последних результата):

> "\uD500".[0] |> uint16 ;;
val it : uint16 = 54528us
> "\uD700".[0] |> uint16 ;;
val it : uint16 = 55040us
> "\uD800".[0] |> uint16 ;;
val it : uint16 = 65533us
> "\uD900".[0] |> uint16 ;;
val it : uint16 = 65533us

К счастью, этот обходной путь работает:

> let s = new System.String( [| char 0xD800 |] )
s.[0] |> uint16
;;

val s : System.String = "�"
val it : uint16 = 55296us

На основе этого вывода я могу построить исправленную (точнее, обходную) версию isSurrogatePair:

let isSurrogatePair input =
  let chrToStr code = new System.String( [| char code |] )
  let regex = "[" + (chrToStr 0xD800) + "-" + (chrToStr 0xDBFF) + "][" + (chrToStr 0xDC00) + "-" + (chrToStr 0xDFFF) + "]"
  Regex.IsMatch(input,  regex)

Эта версия правильно возвращает true для вашего ввода.

Я только что зарегистрировал эту проблему на GitHub: https://github.com/Microsoft/visualfsharp/issues/338

person Fyodor Soikin    schedule 31.03.2015
comment
Для потомков: в последних версиях F# эта проблема решена, литералы больше не проявляют эту проблему кодирования. - person Abel; 26.07.2018

Кажется, это законная ошибка F#, тут нет аргументов. Просто хотел предложить несколько альтернативных обходных путей.


Не встраивайте проблемные символы в саму строку, укажите их, используя обычную поддержку юникода в регулярном выражении. Шаблон регулярного выражения для соответствия кодовой точке Unicode XXXX равен \uXXXX, поэтому просто избегайте обратной косой черты или используйте дословную строку:

Regex.IsMatch(input, "[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]")
// or
Regex.IsMatch(input, @"[\uD800-\uDBFF][\uDC00-\uDFFF]")

Используйте встроенную поддержку регулярных выражений для блоков Unicode:

// high surrogate followed by low surrogate
Regex.IsMatch(input, @"(\p{IsHighSurrogates}|\p{IsHighPrivateUseSurrogates})\p{IsLowSurrogates}")

или свойства

// 2 characters, each of which is half of a surrogate pair
// (maybe could give false-positive if both are, e.g. low-surrogates)
Regex.IsMatch(input, @"\p{Cs}{2}")
person latkin    schedule 01.04.2015