summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorringabout <43030857+ringabout@users.noreply.github.com>2023-05-30 19:40:09 +0800
committerGitHub <noreply@github.com>2023-05-30 13:40:09 +0200
commit40f88da90b0589a91f4f60b2ebf49859b79e2247 (patch)
tree4850674d0e8ce1726f7ab071693e198359d0a801
parent7e055413f9bbec81fc9b7e1542695f2886787566 (diff)
downloadNim-40f88da90b0589a91f4f60b2ebf49859b79e2247.tar.gz
alternative to #21914; split, rsplit now forbid an empty separator (#21961)
-rw-r--r--changelogs/changelog_2_0_0.md1
-rw-r--r--lib/pure/strutils.nim26
-rw-r--r--tests/stdlib/tstrutils.nim14
3 files changed, 37 insertions, 4 deletions
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
index 00c20f397..4a15855f4 100644
--- a/changelogs/changelog_2_0_0.md
+++ b/changelogs/changelog_2_0_0.md
@@ -255,6 +255,7 @@
 
 - Pragma `{.inline.}` generates `__forceinline` if `__has_attribute(__forceinline)` for GCC and Clang.
 
+- `strutils.split` and `strutils.rsplit` now forbid an empty separator.
 
 ## Standard library additions and changes
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 0a77e8bf6..fe9f2a8c3 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -487,12 +487,15 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   ##   "22"
   ##   "08"
   ##   "08.398990"
+  ## 
+  ## .. warning:: `seps` should not be empty.
   ##
   ## See also:
   ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `split func<#split,string,set[char],int>`_
+  assert seps.card > 0, "Empty separator"
   splitCommon(s, seps, maxsplit, 1)
 
 iterator split*(s: string, sep: string, maxsplit: int = -1): string =
@@ -512,11 +515,14 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ##   "is"
   ##   "corrupted"
   ##
+  ## .. warning:: `sep` should not be empty.
+  ##
   ## See also:
   ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `split func<#split,string,string,int>`_
+  assert sep.len > 0, "Empty separator"
   splitCommon(s, sep, maxsplit, sep.len)
 
 
@@ -585,13 +591,16 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the set of chars `seps`
+  ## Substrings are separated from the right by the set of chars `seps`.
+  ## 
+  ## .. warning:: `seps` should not be empty.
   ##
   ## See also:
   ## * `split iterator<#split.i,string,set[char],int>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `rsplit func<#rsplit,string,set[char],int>`_
+  assert seps.card > 0, "Empty separator"
   rsplitCommon(s, seps, maxsplit, 1)
 
 iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
@@ -610,13 +619,16 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the string `sep`
+  ## Substrings are separated from the right by the string `sep`.
+  ## 
+  ## .. warning:: `sep` should not be empty.
   ##
   ## See also:
   ## * `split iterator<#split.i,string,string,int>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `rsplit func<#rsplit,string,string,int>`_
+  assert sep.len > 0, "Empty separator"
   rsplitCommon(s, sep, maxsplit, sep.len)
 
 iterator splitLines*(s: string, keepEol = false): string =
@@ -728,6 +740,8 @@ func split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[
   ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
   ## documentation), but is a func that returns a sequence of substrings.
   ##
+  ## .. warning:: `seps` should not be empty.
+  ##
   ## See also:
   ## * `split iterator <#split.i,string,set[char],int>`_
   ## * `rsplit func<#rsplit,string,set[char],int>`_
@@ -745,6 +759,8 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
   ## Substrings are separated by the string `sep`. This is a wrapper around the
   ## `split iterator <#split.i,string,string,int>`_.
   ##
+  ## .. warning:: `sep` should not be empty.
+  ##
   ## See also:
   ## * `split iterator <#split.i,string,string,int>`_
   ## * `rsplit func<#rsplit,string,string,int>`_
@@ -757,8 +773,6 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
     doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely",
         "", "", "", "spaced", "sentence"]
     doAssert "a  largely    spaced sentence".split(" ", maxsplit = 1) == @["a", " largely    spaced sentence"]
-  doAssert(sep.len > 0)
-
   accResult(split(s, sep, maxsplit))
 
 func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
@@ -808,6 +822,8 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
+  ## .. warning:: `seps` should not be empty.
+  ##
   ## See also:
   ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
   ## * `split func<#split,string,set[char],int>`_
@@ -835,6 +851,8 @@ func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
+  ## .. warning:: `sep` should not be empty.
+  ##
   ## See also:
   ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
   ## * `split func<#split,string,string,int>`_
diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim
index 67eb5cf3a..d53e9d8b4 100644
--- a/tests/stdlib/tstrutils.nim
+++ b/tests/stdlib/tstrutils.nim
@@ -53,6 +53,13 @@ template main() =
     doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example  "]
     doAssert s.split(' ', maxsplit = 1) == @["", "this is an example  "]
     doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example  "]
+    # Empty string:
+    doAssert "".split() == @[""]
+    doAssert "".split(" ") == @[""]
+    doAssert "".split({' '}) == @[""]
+    # Empty separators:
+    doAssertRaises(AssertionDefect): discard s.split({})
+    doAssertRaises(AssertionDefect): discard s.split("")
 
   block: # splitLines
     let fixture = "a\nb\rc\r\nd"
@@ -69,6 +76,13 @@ template main() =
     doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"]
     doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"]
     doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"]
+    # Empty string:
+    doAssert "".rsplit() == @[""]
+    doAssert "".rsplit(" ") == @[""]
+    doAssert "".rsplit({' '}) == @[""]
+    # Empty separators:
+    doAssertRaises(AssertionDefect): discard "".rsplit({})
+    doAssertRaises(AssertionDefect): discard "".rsplit("")
 
   block: # splitWhitespace
     let s = " this is an example  "