首页 > 解决方案 > 标量的 Unicode 字符串的子字符串函数

问题描述

以前,我在字素簇上写了一个 unicode 字符串的子字符串函数,如下所示。例如,传递给函数的位置超过 Unicode 标量,\r\n计为 2,但 Grapheme 簇计\r\n为 1。因此,此函数在某些情况下无法正常工作:

let uni_sub (s: string) (pos: int) (len: int) = 
  let (_, r) = 
    Uuseg_string.fold_utf_8 
      `Grapheme_cluster
      (fun (p, acc) ch -> if (p >= pos) && (p <= pos+len-1) then (p+1, acc ^ ch) else (p+1, acc))
      (0, "")
      s 
    in 
  r

建议我在标量上编写 unicode 字符串的子字符串函数,方法是使用Uutf.String.fold_utf_8and Buffer.add_utf_8_uchar。但是,在不了解系统如何工作的情况下,我只能粗略地编写以下代码,并希望首先使类型工作。

let uni_sub_scalars (s: string) (pos: int) (len: int) = 
  let b: Buffer.t = Buffer.create 42 in
  let rec add (acc: string list) (v: [ `Uchar of Stdlib.Uchar.t | `Await | `End ]) : Uuseg.ret =
    match v with
    | `Uchar u -> 
      Buffer.add_utf_8_uchar b u; 
      add acc `Await
    | `Await | `End -> failwith "don't know what to do"
  in
  let (_, r) = 
    Uuseg_string.fold_utf_8 
      (`Custom (Uuseg.custom ~add:add))
      (fun (p, acc) ch -> if (p >= pos) && (p <= pos+len-1) then (p+1, acc ^ ch) else (p+1, acc))
      (0, "")
      s 
    in 
  r

并且编译返回了一个我不知道如何修复的错误:

File "lib/utility.ml", line 45, characters 6-39:
45 |       (`Custom (Uuseg.custom ~add:add))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type
         [> `Custom of
              ?mandatory:(string list -> bool) ->
              name:string ->
              create:(unit -> string list) ->
              copy:(string list -> string list) -> unit -> Uuseg.custom ]
       but an expression was expected of type [< Uuseg.boundary ]
       Types for tag `Custom are incompatible
make: *** [lib/utility.cmo] Error 2

谁能帮我用标量写这个Unicode字符串的子字符串函数?

标签: unicodeocaml

解决方案


并且编译返回了一个我不知道如何修复的错误:

Uuseg.custom函数创建一个自定义分段器并接受一些参数(您只传递了一个),

val custom :
  ?mandatory:('a -> bool) ->
  name:string ->
  create:(unit -> 'a) ->
  copy:('a -> 'a) ->
  add: ('a -> [ `Uchar of Uchar.t | `Await | `End ] -> ret) -> unit -> custom

因此,您还需要传递name, create,copy参数以及位置()参数。但我不认为这是你应该使用的功能。

谁能帮我用标量写这个Unicode字符串的子字符串函数?

是的,如果我们遵循建议并“通过使用Uutf.String.fold_utf_8and Buffer.add_utf_8_uchar”来实现它,那将非常容易。(注意,我们被建议使用Uutf.String.fold_utf_8not Uuseg_string.fold_utf_8)。

一个简单的实现(不会做很多错误检查),看起来像这样,

let substring s pos len =
  let buf = Buffer.create len in
  let _ : int = Uutf.String.fold_utf_8 (fun off _ elt ->
      match elt with
      | `Uchar x when off >= pos && off < pos + len ->
        Buffer.add_utf_8_uchar buf x;
        off + 1
      | _ -> off + 1) 0 s in
  Buffer.contents buf

这是它的工作原理(以我的名字作为工作示例),

# substring "Иван\n\rГотовчиц" 0 5;;
- : string = "Иван\n"
# substring "Иван\n\rГотовчиц" 11 3;;
- : string = "чиц"

它适用于从右到左的脚本,

# let shalom = substring "שָׁלוֹ";;
val shalom : int -> int -> string = <fun>
# shalom 0 1;;
- : string = "ש"
# shalom 0 2;;
- : string = "שָ"
# shalom 2 2;;
- : string = "ׁל"
# shalom 2 1;;
- : string = "ׁ"

推荐阅读