7

我正在从事 F# 中的一个小型项目,该项目涉及将现有 C# 代码移植到 F#,我似乎遇到了两种语言之间处理正则表达式的方式的差异(我发布这个希望发现我是只是做错了什么)。

这个次要函数使用此处概述的正则表达式技巧简单地检测代理对。这是当前的实现:

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

如果我然后对一个已知的代理对执行它,如下所示:

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

我进入falseFSI 窗口。

如果我使用等效的 C#:

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

和相同的输入值,我(正确)得到true返回。

这是一个真实的问题吗?我只是在我的 F# 实现中做错了吗?

4

2 回答 2

8

There appears to be a bug in how F# encodes escaped Unicode characters.
Here's from the F# Interactive (note the last two results):

> "\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

Fortunately, this workaround works:

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

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

Based on that finding, I can construct a corrected (or, rather, workarounded) version of 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)

This version correctly returns true for your input.

I have just filed this issue on GitHub: https://github.com/Microsoft/visualfsharp/issues/338

于 2015-03-31T04:26:28.717 回答
3

似乎这是一个合法的 F# 错误,没有争论。只是想提出一些替代的解决方法。


不要将问题字符嵌入字符串本身,使用正则表达式的正常 unicode 支持指定它们。匹配 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}")
于 2015-04-01T04:43:57.970 回答