mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-07 03:35:30 -05:00
add a lot of string functions in CCString
This commit is contained in:
parent
79d57f9be7
commit
85cb18751a
2 changed files with 218 additions and 23 deletions
|
|
@ -87,6 +87,30 @@ let is_sub ~sub i s j ~len =
|
|||
if i+len > String.length sub then invalid_arg "String.is_sub";
|
||||
_is_sub ~sub i s j ~len
|
||||
|
||||
(* note: inefficient *)
|
||||
let find ?(start=0) ~sub s =
|
||||
let n = String.length sub in
|
||||
let i = ref start in
|
||||
try
|
||||
while !i + n < String.length s do
|
||||
if _is_sub ~sub 0 s !i ~len:n then raise Exit;
|
||||
incr i
|
||||
done;
|
||||
-1
|
||||
with Exit ->
|
||||
!i
|
||||
|
||||
let rfind ~sub s =
|
||||
let n = String.length sub in
|
||||
let i = ref (String.length s - n) in
|
||||
try
|
||||
while !i >= 0 do
|
||||
if _is_sub ~sub 0 s !i ~len:n then raise Exit;
|
||||
decr i
|
||||
done;
|
||||
~-1
|
||||
with Exit ->
|
||||
!i
|
||||
|
||||
module Split = struct
|
||||
type split_state =
|
||||
|
|
@ -158,20 +182,17 @@ module Split = struct
|
|||
|
||||
let seq ~by s = _mkseq ~by s _tuple3
|
||||
let seq_cpy ~by s = _mkseq ~by s String.sub
|
||||
end
|
||||
|
||||
(* note: inefficient *)
|
||||
let find ?(start=0) ~sub s =
|
||||
let n = String.length sub in
|
||||
let i = ref start in
|
||||
try
|
||||
while !i + n < String.length s do
|
||||
if _is_sub ~sub 0 s !i ~len:n then raise Exit;
|
||||
incr i
|
||||
done;
|
||||
-1
|
||||
with Exit ->
|
||||
!i
|
||||
let left ~by s =
|
||||
let i = find ~sub:by s in
|
||||
if i = ~-1 then None
|
||||
else Some (String.sub s 0 i, String.sub s (i+1) (String.length s - i - 1))
|
||||
|
||||
let right ~by s =
|
||||
let i = rfind ~sub:by s in
|
||||
if i = ~-1 then None
|
||||
else Some (String.sub s 0 i, String.sub s (i+1) (String.length s - i - 1))
|
||||
end
|
||||
|
||||
let repeat s n =
|
||||
assert (n>=0);
|
||||
|
|
@ -252,11 +273,6 @@ let of_list l =
|
|||
List.iter (Buffer.add_char buf) l;
|
||||
Buffer.contents buf
|
||||
|
||||
(*$T
|
||||
of_list ['a'; 'b'; 'c'] = "abc"
|
||||
of_list [] = ""
|
||||
*)
|
||||
|
||||
let of_array a =
|
||||
init (Array.length a) (fun i -> a.(i))
|
||||
|
||||
|
|
@ -285,11 +301,80 @@ let set s i c =
|
|||
if i<0 || i>= String.length s then invalid_arg "CCString.set";
|
||||
init (String.length s) (fun j -> if i=j then c else s.[j])
|
||||
|
||||
(*$T
|
||||
set "abcd" 1 '_' = "a_cd"
|
||||
set "abcd" 0 '-' = "-bcd"
|
||||
(try set "abc" 5 '_'; false with Invalid_argument _ -> true)
|
||||
*)
|
||||
let iter = String.iter
|
||||
|
||||
#if OCAML_MAJOR >= 4
|
||||
|
||||
let map = String.map
|
||||
let mapi = String.mapi
|
||||
let iteri = String.iteri
|
||||
|
||||
#else
|
||||
|
||||
let map f s = init (length s) (fun i -> f s.[i])
|
||||
let mapi f s = init (length s) (fun i -> f i s.[i])
|
||||
|
||||
let iteri f s =
|
||||
for i = 0 to String.length s - 1 do
|
||||
f i s.[i]
|
||||
done
|
||||
|
||||
#endif
|
||||
|
||||
let flat_map ?sep f s =
|
||||
let buf = Buffer.create (String.length s) in
|
||||
iteri
|
||||
(fun i c ->
|
||||
begin match sep with
|
||||
| Some _ when i=0 -> ()
|
||||
| None -> ()
|
||||
| Some sep -> Buffer.add_string buf sep
|
||||
end;
|
||||
Buffer.add_string buf (f c)
|
||||
) s;
|
||||
Buffer.contents buf
|
||||
|
||||
exception MyExit
|
||||
|
||||
let for_all p s =
|
||||
try iter (fun c -> if not (p c) then raise MyExit) s; true
|
||||
with MyExit -> false
|
||||
|
||||
let exists p s =
|
||||
try iter (fun c -> if p c then raise MyExit) s; false
|
||||
with MyExit -> true
|
||||
|
||||
let map2 f s1 s2 =
|
||||
if length s1 <> length s2 then invalid_arg "String.map2";
|
||||
init (String.length s1) (fun i -> f s1.[i] s2.[i])
|
||||
|
||||
let iter2 f s1 s2 =
|
||||
if length s1 <> length s2 then invalid_arg "String.iter2";
|
||||
for i = 0 to String.length s1 - 1 do
|
||||
f s1.[i] s2.[i]
|
||||
done
|
||||
|
||||
let iteri2 f s1 s2 =
|
||||
if length s1 <> length s2 then invalid_arg "String.iteri2";
|
||||
for i = 0 to String.length s1 - 1 do
|
||||
f i s1.[i] s2.[i]
|
||||
done
|
||||
|
||||
let fold2 f acc s1 s2 =
|
||||
if length s1 <> length s2 then invalid_arg "String.fold2";
|
||||
let rec fold' acc s1 s2 i =
|
||||
if i = String.length s1 then acc
|
||||
else fold' (f acc s1.[i] s2.[i]) s1 s2 (i+1)
|
||||
in
|
||||
fold' acc s1 s2 0
|
||||
|
||||
let for_all2 p s1 s2 =
|
||||
try iter2 (fun c1 c2 -> if not (p c1 c2) then raise MyExit) s1 s2; true
|
||||
with MyExit -> false
|
||||
|
||||
let exists2 p s1 s2 =
|
||||
try iter2 (fun c1 c2 -> if p c1 c2 then raise MyExit) s1 s2; false
|
||||
with MyExit -> true
|
||||
|
||||
let pp buf s =
|
||||
Buffer.add_char buf '"';
|
||||
|
|
|
|||
|
|
@ -81,12 +81,35 @@ val of_klist : char klist -> string
|
|||
val of_list : char list -> string
|
||||
val of_array : char array -> string
|
||||
|
||||
(*$T
|
||||
of_list ['a'; 'b'; 'c'] = "abc"
|
||||
of_list [] = ""
|
||||
*)
|
||||
|
||||
val to_array : string -> char array
|
||||
|
||||
val find : ?start:int -> sub:string -> string -> int
|
||||
(** Find [sub] in string, returns its first index or [-1].
|
||||
Should only be used with very small [sub] *)
|
||||
|
||||
(*$T
|
||||
find ~sub:"bc" "abcd" = 1
|
||||
find ~sub:"bc" "abd" = ~-1
|
||||
find ~sub:"a" "_a_a_a_" = 1
|
||||
*)
|
||||
|
||||
val rfind : sub:string -> string -> int
|
||||
(** Find [sub] in string from the right, returns its first index or [-1].
|
||||
Should only be used with very small [sub]
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
(*$T
|
||||
rfind ~sub:"bc" "abcd" = 1
|
||||
rfind ~sub:"bc" "abd" = ~-1
|
||||
rfind ~sub:"a" "_a_a_a_" = 5
|
||||
rfind ~sub:"bc" "abcdbcd" = 4
|
||||
*)
|
||||
|
||||
val is_sub : sub:string -> int -> string -> int -> len:int -> bool
|
||||
(** [is_sub ~sub i s j ~len] returns [true] iff the substring of
|
||||
[sub] starting at position [i] and of length [len] *)
|
||||
|
|
@ -143,8 +166,75 @@ val set : string -> int -> char -> string
|
|||
@raise Invalid_argument if [i] is an invalid index
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
(*$T
|
||||
set "abcd" 1 '_' = "a_cd"
|
||||
set "abcd" 0 '-' = "-bcd"
|
||||
(try ignore (set "abc" 5 '_'); false with Invalid_argument _ -> true)
|
||||
*)
|
||||
|
||||
val iter : (char -> unit) -> string -> unit
|
||||
(** Alias to {!String.iter}
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val iteri : (int -> char -> unit) -> string -> unit
|
||||
(** iter on chars with their index
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val map : (char -> char) -> string -> string
|
||||
(** map chars
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val mapi : (int -> char -> char) -> string -> string
|
||||
(** map chars with their index
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val flat_map : ?sep:string -> (char -> string) -> string -> string
|
||||
(** map each chars to a string, then concatenates them all
|
||||
@param sep optional separator between each generated string
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val for_all : (char -> bool) -> string -> bool
|
||||
(** true for all chars?
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val exists : (char -> bool) -> string -> bool
|
||||
(** true for some char?
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
include S with type t := string
|
||||
|
||||
(** {2 Operations on 2 strings} *)
|
||||
|
||||
val map2 : (char -> char -> char) -> string -> string -> string
|
||||
(** map pairs of chars
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val iter2: (char -> char -> unit) -> string -> string -> unit
|
||||
(** iterate on pairs of chars
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val iteri2: (int -> char -> char -> unit) -> string -> string -> unit
|
||||
(** iterate on pairs of chars with their index
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val fold2: ('a -> char -> char -> 'a) -> 'a -> string -> string -> 'a
|
||||
(** fold on pairs of chars
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val for_all2 : (char -> char -> bool) -> string -> string -> bool
|
||||
(** all pair of chars respect the predicate?
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
val exists2 : (char -> char -> bool) -> string -> string -> bool
|
||||
(** exists a pair of chars?
|
||||
@raises Invalid_argument if the strings have not the same length
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
(** {2 Splitting} *)
|
||||
|
||||
module Split : sig
|
||||
|
|
@ -181,6 +271,26 @@ module Split : sig
|
|||
val seq_cpy : by:string -> string -> string sequence
|
||||
|
||||
val klist_cpy : by:string -> string -> string klist
|
||||
|
||||
val left : by:string -> string -> (string * string) option
|
||||
(** Split on the first occurrence of [by] from the left-most part of
|
||||
the string
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
(*$T
|
||||
Split.left ~by:" " "ab cde f g " = Some ("ab", "cde f g ")
|
||||
Split.left ~by:"_" "abcde" = None
|
||||
*)
|
||||
|
||||
val right : by:string -> string -> (string * string) option
|
||||
(** Split on the first occurrence of [by] from the rightmost part of
|
||||
the string
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
(*$T
|
||||
Split.right ~by:" " "ab cde f g" = Some ("ab cde f", "g")
|
||||
Split.right ~by:"_" "abcde" = None
|
||||
*)
|
||||
end
|
||||
|
||||
(** {2 Slices} A contiguous part of a string *)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue