Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String suffix and prefix functions. #9533

Merged
merged 2 commits into from
Jun 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ Working version
and more generally all other data that is not a well-formed OCaml value
(Xavier Leroy, review by Damien Doligez and Gabriel Scherer)

- #9533: Added String.starts_with and String.ends_with.
(Bernhard Schommer, review by Daniel Bünzli, Gabriel Scherer and
Alain Frisch)
bschommer marked this conversation as resolved.
Show resolved Hide resolved

### Other libraries:

* #9206, #9419: update documentation of the threads library;
Expand Down
4 changes: 1 addition & 3 deletions stdlib/filename.ml
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,7 @@ module Unix : SYSDEPS = struct
&& (String.length n < 2 || String.sub n 0 2 <> "./")
&& (String.length n < 3 || String.sub n 0 3 <> "../")
let check_suffix name suff =
String.length name >= String.length suff &&
String.sub name (String.length name - String.length suff)
(String.length suff) = suff
String.ends_with ~suffix:suff name

let chop_suffix_opt ~suffix filename =
let len_s = String.length suffix and len_f = String.length filename in
Expand Down
19 changes: 19 additions & 0 deletions stdlib/string.ml
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,22 @@ let to_seq s = bos s |> B.to_seq
let to_seqi s = bos s |> B.to_seqi

let of_seq g = B.of_seq g |> bts

let starts_with ~prefix s =
let len_s = length s
and len_pre = length prefix in
let rec aux i =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this will allocate a closure
extlib uses the following implementation : https://github.com/ygrek/ocaml-extlib/blob/master/src/extString.ml#L43

if i = len_pre then true
else if unsafe_get s i <> unsafe_get prefix i then false
else aux (i + 1)
in len_s >= len_pre && aux 0

let ends_with ~suffix s =
let len_s = length s
and len_suf = length suffix in
let diff = len_s - len_suf in
let rec aux i =
if i = len_suf then true
else if unsafe_get s (diff + i) <> unsafe_get suffix i then false
else aux (i + 1)
in diff >= 0 && aux 0
8 changes: 8 additions & 0 deletions stdlib/string.mli
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ val equal: t -> t -> bool
(** The equal function for strings.
@since 4.03.0 *)

val starts_with : prefix:t -> t -> bool
(** [String.starts_with prefix s] tests if [s] starts with [prefix]
@since 4.12.0 *)

val ends_with : suffix:t -> t -> bool
(** [String.ends_with suffix s] tests if [s] ends with [suffix]
@since 4.12.0 *)

val split_on_char: char -> string -> string list
(** [String.split_on_char sep s] returns the list of all (possibly empty)
substrings of [s] that are delimited by the [sep] character.
Expand Down
8 changes: 8 additions & 0 deletions stdlib/stringLabels.mli
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ val equal: t -> t -> bool
(** The equal function for strings.
@since 4.05.0 *)

val starts_with : prefix:t -> t -> bool
(** [String.starts_with prefix s] tests if [s] starts with [prefix]
@since 4.12.0 *)

val ends_with : suffix:t -> t -> bool
(** [String.ends_with suffix s] tests if [s] ends with [suffix]
@since 4.12.0 *)

val split_on_char: sep:char -> string -> string list
(** [String.split_on_char sep s] returns the list of all (possibly empty)
substrings of [s] that are delimited by the [sep] character.
Expand Down
14 changes: 13 additions & 1 deletion testsuite/tests/lib-string/test_string.ml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,17 @@ let () =
while !sz >= 0 do push big l; sz += Sys.max_string_length done;
while !sz <= 0 do push big l; sz += Sys.max_string_length done;
try ignore (String.concat "" !l); assert false
with Invalid_argument _ -> ()
with Invalid_argument _ -> ();
assert(String.starts_with ~prefix:"foob" "foobarbaz");
assert(String.starts_with ~prefix:"" "foobarbaz");
assert(String.starts_with ~prefix:"" "");
assert(not (String.starts_with ~prefix:"foobar" "bar"));
assert(not (String.starts_with ~prefix:"foo" ""));
assert(not (String.starts_with ~prefix:"fool" "foobar"));
assert(String.ends_with ~suffix:"baz" "foobarbaz");
assert(String.ends_with ~suffix:"" "foobarbaz");
assert(String.ends_with ~suffix:"" "");
assert(not (String.ends_with ~suffix:"foobar" "bar"));
assert(not (String.ends_with ~suffix:"foo" ""));
assert(not (String.ends_with ~suffix:"obaz" "foobar"));
end