txt: ## dist_zef-lizmat-String-Utils.md
  [![Actions Status](https://github.com/lizmat/String-Utils/actions/workflows/linux.yml/badge.svg)](https://github.com/lizmat/String-Utils/actions) [![Actions Status](https://github.com/lizmat/String-Utils/actions/workflows/macos.yml/badge.svg)](https://github.com/lizmat/String-Utils/actions) [![Actions Status](https://github.com/lizmat/String-Utils/actions/workflows/windows.yml/badge.svg)](https://github.com/lizmat/String-Utils/actions)
  
  # NAME
  
  String::Utils - Provide some optimized string functions
  
  # SYNOPSIS
  
  ```
  use String::Utils;
  
  say before("foobar","bar");            # foo
  
  say between("foobarbaz","foo","baz");  # bar
  
  say between-included("foobarbaz","oo","baz");  # oobarbaz
  
  say around("foobarbaz", "ob", "rb");   # foaz
  
  say after("foobar","foo");             # bar
  
  say chomp-needle("foobarbaz", "baz");  # foobar
  
  say root <abcd abce abde>;             # ab
  
  say leaf <zip.txt zop.txt ff.txt>;     # .txt
  
  say is-sha1 "foo bar baz";             # False
  
  say stem "foo.tar.gz";                 # foo
  say stem "foo.tar.gz", 1;              # foo.tar
  
  say ngram "foobar", 3;                 # foo oob oba bar
  
  say non-word "foobar";                 # False
  say non-word "foo/bar";                # True
  
  say letters("//foo:bar");              # foobar
  
  say nomark("élève");                   # eleve
  
  say has-marks("foo👩🏽‍💻bar");             # False
  say has-marks("fóöbar");               # True
  
  dd leading-whitespace(" \t foo");      # " \t "
  dd trailing-whitespace("bar \t ");     # " \t "
  say is-whitespace("\t \n");            # True
  say is-whitespace("\ta\n");            # False
  say is-whitespace("");                 # True
  
  say is-uppercase("FOOBAR");            # True
  say is-uppercase("FooBar");            # False
  say is-uppercase("");                  # True
  
  say is-lowercase("foobar");            # True
  say is-lowercase("FooBar");            # False
  say is-lowercase("");                  # True
  
  say consists-of("aaabbcc", "abc");     # True
  say consists-of("aaadbcc", "abc");     # False
  say consists-of("", "abc");            # True
  
  say all-same("aaaaaa");                # "a"
  say all-same("aaaaba");                # Nil
  say all-same("");                      # Nil
  
  .say for paragraphs("a\n\nb");         # 0 => a␤2 => b␤
  .say for paragraphs($path.IO.lines);   # …
  
  my $string = "foo";
  my $regex  = regexify($string, :ignorecase);
  say "FOOBAR" ~~ $regex;                # ｢FOO｣
  
  dd expand-tab("a\tbb\tccc",4);         # "a   bb  ccc"
  
  say word-at("foo bar baz", 5);         # (4 3)
  
  say abbrev(<yes no>).keys.sort;        # (n no y ye yes)
  
  use String::Utils <before after>;  # only import "before" and "after"
  
  ```
  
  # DESCRIPTION
  
  String::Utils provides some simple string functions that are not (yet) provided by the core Raku Programming Language.
  
  These functions are implemented **without** using regexes for speed.
  
  # SELECTIVE IMPORTING
  
  ```
  use String::Utils <before after>;  # only import "before" and "after"
  
  ```
  
  By default all utility functions are exported. But you can limit this to the functions you actually need by specifying the names in the `use` statement.
  
  To prevent name collisions and/or import any subroutine with a more memorable name, one can use the "original-name:known-as" syntax. A semi-colon in a specified string indicates the name by which the subroutine is known in this distribution, followed by the name with which it will be known in the lexical context in which the `use` command is executed.
  
  ```
  use String::Utils <root:common-start>;  # import "root" as "common-start"
  
  say common-start <abcd abce abde>;  # ab
  
  ```
  
  # SUBROUTINES
  
  ## after
  
  ```
  say after("foobar","foo");   # bar
  
  say "foobar".&after("foo");  # bar
  
  say after("foobar","goo");   # Nil
  
  ```
  
  Return the string **after** a given string, or `Nil` if the given string could not be found. The equivalent of the stringification of `/ <?after foo> .* /`.
  
  ## around
  
  ```
  say around("foobarbaz","ob","rb");     # foaz
  
  say "foobarbaz".&around("ob","rb");    # foaz
  
  say around("foobarbaz","goo","baz");   # foobarbaz
  
  ```
  
  Return the string **around** two given strings, or the string itself if either of the bounding strings could not be found. The equivalent of `.subst: / <?after ob> .*? <?before rb> /`.
  
  ## before
  
  ```
  say before("foobar","bar");   # foo
  
  say "foobar".&before("bar");  # foo
  
  say before("foobar","baz");   # Nil
  
  ```
  
  Return the string **before** a given string, or `Nil` if the given string could not be found. The equivalent of the stringification of `/ .*? <?before bar> /`.
  
  ## between
  
  ```
  say between("foobarbaz","foo","baz");   # bar
  
  say "foobarbaz".&between("foo","baz");  # bar
  
  say between("foobarbaz","goo","baz");   # Nil
  
  ```
  
  Return the string **between** two given strings, or `Nil` if either of the bounding strings could not be found. The equivalent of the stringification of `/ <?after foo> .*? <?before baz> /`.
  
  ## between-included
  
  ```
  say between-included("foobarbaz","oo","baz");   # oobarbaz
  
  say "foobarbaz".&between-included("oo","baz");  # oobarbaz
  
  say between-included("foobarbaz","goo","baz");  # Nil
  
  ```
  
  Return the string **between** two given strings **including** the given strings, or `Nil` if either of the bounding strings could not be found. The equivalent of the stringification of `/ o .*? baz /`.
  
  ## chomp-needle
  
  ```
  say chomp-needle("foobarbaz","baz");   # foobar
  
  say "foobarbaz".&chomp-needle("baz");  # foobar
  
  say chomp-needle("foobarbaz","bar");   # foobarbaz
  
  ```
  
  Return the string without the given target string at the end, or the string itself if the target string is not at the end. The equivalent of `.subst(/ baz $/)`.
  
  ## root
  
  ```
  say root <abcd abce abde>;  # ab
  
  ```
  
  Return the common **beginning** of the given strings, or the empty string if no common string could be found. See also `leaf`.
  
  ## leaf
  
  ```
  say leaf <zip.txt zop.txt ff.txt>;  # .txt
  
  ```
  
  Return the common **end** of the given strings, or the empty string if no common string could be found. See also `root`.
  
  ## is-sha1
  
  ```
  say is-sha1 "abcd abce abde";  # False
  say is-sha1 "356A192B7913B04C54574D18C28D46E6395428AB";  # True
  
  ```
  
  Return a `Bool` indicating whether the given string is a SHA1 string (40 chars and only containing 0123456789ABCDEF).
  
  ## stem
  
  ```
  say stem "foo.tar.gz";     # foo
  say stem "foo.tar.gz", 1;  # foo.tar
  say stem "foo.tar.gz", *;  # foo
  
  ```
  
  Return the stem of a string with all of its extensions removed. Optionally accepts a second argument indicating the number of extensions to be removed. This may be `*` (aka `Whatever`) to indicate to remove all extensions.
  
  ## ngram
  
  ```
  say ngram "foobar", 3;            # foo oob oba bar
  
  say ngram "foobar", 4, :partial;  # foob ooba obar bar ar r
  
  ```
  
  Return a sequence of substrings of the given size, while only moving up one position at a time in the original string. Optionally takes a `:partial` flag to also produce incomplete substrings at the end of the sequence.
  
  ## non-word
  
  ```
  say non-word "foobar";   # False
  
  say non-word "foo/bar";  # True
  
  ```
  
  Returns a `Bool` indicating whether the string contained **any** non-word characters.
  
  ## letters
  
  ```
  say letters("//foo:bar");  # foobar
  
  ```
  
  Returns all of the alphanumeric characters in the given string as a string.
  
  ## nomark
  
  ```
  say nomark("élève");  # eleve
  
  ```
  
  Returns the given string with any diacritcs removed.
  
  ## has-marks
  
  ```
  say has-marks("foo👩🏽‍💻bar");             # False
  say has-marks("fóöbar");               # True
  
  ```
  
  Returns a `Bool` indicating whether the given string contains any alphanumeric characters with marks (accents).
  
  ## leading-whitespace
  
  ```
  dd leading-whitespace("foo");      # ""
  dd leading-whitespace(" \t foo");  # " \t "
  dd leading-whitespace(" \t ");     # " \t "
  
  ```
  
  Returns a `Str` containing any leading whitespace of the given string.
  
  ## trailing-whitespace
  
  ```
  dd trailing-whitespace("bar");      # ""
  dd trailing-whitespace("bar \t ");  # " \t "
  dd trailing-whitespace(" \t ");     # " \t "
  
  ```
  
  Returns a `Str` containing any trailing whitespace of the given string.
  
  ## is-whitespace
  
  ```
  say is-whitespace("\t \n");  # True
  say is-whitespace("\ta\n");  # False
  say is-whitespace("");       # True
  
  ```
  
  Returns a `Bool` indicating whether the string consists of just whitespace characters, or is empty.
  
  ## is-uppercase
  
  ```
  say is-uppercase("FOOBAR");  # True
  say is-uppercase("FooBar");  # False
  say is-uppercase("");        # True
  
  ```
  
  Returns a `Bool` indicating whether the string consists of just uppercase characters, or is empty.
  
  ## is-lowercase
  
  ```
  say is-lowercase("foobar");  # True
  say is-lowercase("FooBar");  # False
  say is-lowercase("");        # True
  
  ```
  
  Returns a `Bool` indicating whether the string consists of just lowercase characters, or is empty.
  
  ## consists-of
  
  ```
  say consists-of("aaabbcc", "abc");     # True
  say consists-of("aaadbcc", "abc");     # False
  say consists-of("", "abc");            # True
  
  ```
  
  Returns a `Bool` indicating whether the string given as the first positional argument only consists of characters given as the second positional argument, or is empty.
  
  ## all-same
  
  ```
  say all-same("aaaaaa");                # "a"
  say all-same("aaaaba");                # Nil
  say all-same("");                      # Nil
  
  ```
  
  If the given string consists of a single character, returns that character. Else returns `Nil`.
  
  ## paragraphs
  
  ```
  .say for paragraphs($path.IO.lines);   # …
  .say for paragraphs("a\n\nb");         # 0 => a␤2 => b␤
  .say for paragraphs("a\n\nb", 1);      # 1 => a␤3 => b␤
  
  ```
  
  Lazily produces a `Seq` of `Pairs` with paragraphs from a `Seq` or string in which the key is the line number where the paragraph starts, and the value is the paragraph (without **last** trailing newline).
  
  The optional second argument can be used to indicate the ordinal number of the first line in the string.
  
  ```
  my class A is Pair { }
  .say for paragraphs("a\n\nb", 1, :Pair(A));  # 1 => a␤␤3 => b␤
  
  ```
  
  Also takes an optional named argument `:Pair` that indicates the class with which the objects should be created. This defailts to the core `Pair` class.
  
  ## regexify
  
  ```
  my $string = "foo";
  my $regex  = regexify($string, :ignorecase);
  say "FOOBAR" ~~ $regex;  # ｢FOO｣
  
  ```
  
  Produce a `Regex` object from a given string and modifiers. Note that this is similar to the `/ <$string> /` syntax. But opposed to that syntax, which interpolates the contents of the string **each time** the regex is executed, the `Regex` object returned by `regexify` is immutable.
  
  The following modifiers are supported:
  
  ### i / ignorecase
  
  ```
  # accept haystack if "bar" is found, regardless of case
  my $regex = regexify("bar", :i);  # or :ignorecase
  
  ```
  
  Allow characters to match even if they are of mixed case.
  
  ### smartcase
  
  ```
  # accept haystack if "bar" is found, regardless of case
  my &anycase = regexify("bar", :smartcase);
  
  # accept haystack if "Bar" is found
  my &exactcase = regexify("Bar", :smartcase);
  
  ```
  
  If the needle is a string and does **not** contain any uppercase characters, then `ignorecase` semantics will be assumed.
  
  ### m / ignoremark
  
  ```
  # accept haystack if "bar" is found, regardless of any accents
  my &anycase = regexify("bar", :m);  # or :ignoremark
  
  ```
  
  Allow characters to match even if they have accents (or not).
  
  ### smartmark
  
  ```
  # accept haystack if "bar" is found, regardless of any accents
  my &anymark = regexify("bar", :smartmark);
  
  # accept haystack if "bår" is found
  my &exactmark = regexify("bår", :smartmark);
  
  ```
  
  If the needle is a string and does **not** contain any characters with accents, then `ignoremark` semantics will be assumed.
  
  ## expand-tab
  
  ```
  dd expand-tab("a\tbb\tccc",4);  # "a   bb  ccc"
  
  ```
  
  Expand any tabs in a string (the first argument) to the given tab width (the second argument). If there are no tabs, then the given string will be returned unaltered.
  
  If the tab width is **zero** or **negative**, will remove any tabs from the string. If the tab width is **one**, then all tabs will be replaced by spaces.
  
  ## word-at
  
  ```
  say word-at("foo bar baz", 5);  # (4 3 1)
  
  ```
  
  Returns a `List` with the start position, the number of characters of the word, and the ordinal number of the word found in the given string at the given position, or directly before it (using `.words` semantics).
  
  Returns `Empty` if no word could be found at the given position, or the position was out of range.
  
  ## abbrev
  
  ```
  say abbrev(<yes no>).keys.sort;       # (n no y ye yes)
  
  say abbrev(<foo bar baz>).keys.sort;  # (bar baz f fo foo)
  
  ```
  
  Takes 0 or more strings as arguments, and returns a `Map` with all shortest possible unambigious versions of the given strings as keys, and their associated original string as the value.
  
  Inspired by Text::Abbrev module by KamilaBorowska.
  
  # AUTHOR
  
  Elizabeth Mattijsen [liz@raku.rocks](mailto:liz@raku.rocks)
  
  Source can be located at: <https://github.com/lizmat/String-Utils> . Comments and Pull Requests are welcome.
  
  If you like this module, or what I’m doing more generally, committing to a [small sponsorship](https://github.com/sponsors/lizmat/) would mean a great deal to me!
  
  # COPYRIGHT AND LICENSE
  
  Copyright 2022, 2023, 2024 Elizabeth Mattijsen
  
  This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.
  