add a lot of string functions in CCString

This commit is contained in:
Simon Cruanes 2015-06-11 15:02:00 +02:00
parent 79d57f9be7
commit 85cb18751a
2 changed files with 218 additions and 23 deletions

View file

@ -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 '"';

View file

@ -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 *)