Skip to content

Commit 3f38f8f

Browse files
authored
add strbasics.strip (#16280)
1 parent 46bd222 commit 3f38f8f

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ provided by the operating system.
171171
dumping (on select signals) and notifying the parent process about the cause
172172
of termination.
173173

174+
- Added `strip` and `setSlice` to `std/strbasics`.
175+
174176

175177
## Language changes
176178

lib/pure/strutils.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,6 +2753,7 @@ func strip*(s: string, leading = true, trailing = true,
27532753
## If both are false, the string is returned unchanged.
27542754
##
27552755
## See also:
2756+
## * `strip proc<strbasics.html#strip,string,set[char]>`_ Inplace version.
27562757
## * `stripLineEnd func<#stripLineEnd,string>`_
27572758
runnableExamples:
27582759
let a = " vhellov "

lib/std/strbasics.nim

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#
2+
#
3+
# The Nim Compiler
4+
# (c) Copyright 2021 Nim Contributors
5+
#
6+
# See the file "copying.txt", included in this
7+
# distribution, for details about the copyright.
8+
#
9+
10+
## This module provides some high performance string operations.
11+
12+
const whitespaces = {' ', '\t', '\v', '\r', '\l', '\f'}
13+
14+
func stripSlice(s: openArray[char], leading = true, trailing = true, chars: set[char] = whitespaces): Slice[int] =
15+
## Returns the slice range of `s` which is stripped `chars`.
16+
runnableExamples:
17+
assert stripSlice(" abc ") == 1 .. 3
18+
var
19+
first = 0
20+
last = high(s)
21+
if leading:
22+
while first <= last and s[first] in chars: inc(first)
23+
if trailing:
24+
while last >= first and s[last] in chars: dec(last)
25+
result = first .. last
26+
27+
func setSlice*(s: var string, slice: Slice[int]) =
28+
## Inplace version of `substr`.
29+
runnableExamples:
30+
import std/sugar
31+
32+
var a = "Hello, Nim!"
33+
doassert a.dup(setSlice(7 .. 9)) == "Nim"
34+
doAssert a.dup(setSlice(0 .. 0)) == "H"
35+
doAssert a.dup(setSlice(0 .. 1)) == "He"
36+
doAssert a.dup(setSlice(0 .. 10)) == a
37+
doAssert a.dup(setSlice(1 .. 0)).len == 0
38+
doAssert a.dup(setSlice(20 .. -1)).len == 0
39+
40+
41+
doAssertRaises(AssertionDefect):
42+
discard a.dup(setSlice(-1 .. 1))
43+
44+
doAssertRaises(AssertionDefect):
45+
discard a.dup(setSlice(1 .. 11))
46+
47+
48+
let first = slice.a
49+
let last = slice.b
50+
51+
assert first >= 0
52+
assert last <= s.high
53+
54+
if first > last:
55+
s.setLen(0)
56+
return
57+
template impl =
58+
for index in first .. last:
59+
s[index - first] = s[index]
60+
if first > 0:
61+
when nimvm: impl()
62+
else:
63+
# not JS and not Nimscript
64+
when not declared(moveMem):
65+
impl()
66+
else:
67+
moveMem(addr s[0], addr s[first], last - first + 1)
68+
s.setLen(last - first + 1)
69+
70+
func strip*(a: var string, leading = true, trailing = true, chars: set[char] = whitespaces) {.inline.} =
71+
## Inplace version of `strip`. Strips leading or
72+
## trailing `chars` (default: whitespace characters).
73+
##
74+
## If `leading` is true (default), leading `chars` are stripped.
75+
## If `trailing` is true (default), trailing `chars` are stripped.
76+
## If both are false, the string is unchanged.
77+
runnableExamples:
78+
var a = " vhellov "
79+
strip(a)
80+
assert a == "vhellov"
81+
82+
a = " vhellov "
83+
a.strip(leading = false)
84+
assert a == " vhellov"
85+
86+
a = " vhellov "
87+
a.strip(trailing = false)
88+
assert a == "vhellov "
89+
90+
var c = "blaXbla"
91+
c.strip(chars = {'b', 'a'})
92+
assert c == "laXbl"
93+
c = "blaXbla"
94+
c.strip(chars = {'b', 'a', 'l'})
95+
assert c == "X"
96+
97+
setSlice(a, stripSlice(a, leading, trailing, chars))

tests/stdlib/tstrbasics.nim

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
discard """
2+
targets: "c cpp js"
3+
"""
4+
5+
import std/[strbasics, sugar]
6+
7+
8+
proc teststrip() =
9+
var a = " vhellov "
10+
strip(a)
11+
doAssert a == "vhellov"
12+
13+
a = " vhellov "
14+
a.strip(leading = false)
15+
doAssert a == " vhellov"
16+
17+
a = " vhellov "
18+
a.strip(trailing = false)
19+
doAssert a == "vhellov "
20+
21+
a.strip()
22+
a.strip(chars = {'v'})
23+
doAssert a == "hello"
24+
25+
a = " vhellov "
26+
a.strip()
27+
a.strip(leading = false, chars = {'v'})
28+
doAssert a == "vhello"
29+
30+
var c = "blaXbla"
31+
c.strip(chars = {'b', 'a'})
32+
doAssert c == "laXbl"
33+
c = "blaXbla"
34+
c.strip(chars = {'b', 'a', 'l'})
35+
doAssert c == "X"
36+
37+
block:
38+
var a = "xxxxxx"
39+
a.strip(chars={'x'})
40+
doAssert a.len == 0
41+
42+
block:
43+
var a = "x"
44+
a.strip(chars={'x'})
45+
doAssert a.len == 0
46+
47+
block:
48+
var a = "x"
49+
a.strip(chars={'1'})
50+
doAssert a.len == 1
51+
52+
block:
53+
var a = ""
54+
a.strip(chars={'x'})
55+
doAssert a.len == 0
56+
57+
block:
58+
var a = "xxx xxx"
59+
a.strip(chars={'x'})
60+
doAssert a == " "
61+
62+
block:
63+
var a = "xxx wind"
64+
a.strip(chars={'x'})
65+
doAssert a == " wind"
66+
67+
block:
68+
var a = "xxx iii"
69+
a.strip(chars={'i'})
70+
doAssert a == "xxx "
71+
72+
block:
73+
var a = "xxx iii"
74+
doAssert a.dup(strip(chars = {'i'})) == "xxx "
75+
doAssert a.dup(strip(chars = {' '})) == "xxx iii"
76+
doAssert a.dup(strip(chars = {'x'})) == " iii"
77+
doAssert a.dup(strip(chars = {'x', ' '})) == "iii"
78+
doAssert a.dup(strip(chars = {'x', 'i'})) == " "
79+
doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
80+
81+
block:
82+
var a = "x i"
83+
doAssert a.dup(strip(chars = {'i'})) == "x "
84+
doAssert a.dup(strip(chars = {' '})) == "x i"
85+
doAssert a.dup(strip(chars = {'x'})) == " i"
86+
doAssert a.dup(strip(chars = {'x', ' '})) == "i"
87+
doAssert a.dup(strip(chars = {'x', 'i'})) == " "
88+
doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
89+
90+
block:
91+
var a = ""
92+
doAssert a.dup(strip(chars = {'i'})).len == 0
93+
doAssert a.dup(strip(chars = {' '})).len == 0
94+
doAssert a.dup(strip(chars = {'x'})).len == 0
95+
doAssert a.dup(strip(chars = {'x', ' '})).len == 0
96+
doAssert a.dup(strip(chars = {'x', 'i'})).len == 0
97+
doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
98+
99+
block:
100+
var a = " "
101+
doAssert a.dup(strip(chars = {'i'})) == " "
102+
doAssert a.dup(strip(chars = {' '})).len == 0
103+
doAssert a.dup(strip(chars = {'x'})) == " "
104+
doAssert a.dup(strip(chars = {'x', ' '})).len == 0
105+
doAssert a.dup(strip(chars = {'x', 'i'})) == " "
106+
doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
107+
108+
109+
block:
110+
var a = "Hello, Nim!"
111+
doassert a.dup(setSlice(7 .. 9)) == "Nim"
112+
doAssert a.dup(setSlice(0 .. 0)) == "H"
113+
doAssert a.dup(setSlice(0 .. 1)) == "He"
114+
doAssert a.dup(setSlice(0 .. 10)) == a
115+
doAssert a.dup(setSlice(1 .. 0)).len == 0
116+
doAssert a.dup(setSlice(20 .. -1)).len == 0
117+
118+
119+
doAssertRaises(AssertionDefect):
120+
discard a.dup(setSlice(-1 .. 1))
121+
122+
doAssertRaises(AssertionDefect):
123+
discard a.dup(setSlice(1 .. 11))
124+
125+
static: teststrip()
126+
teststrip()

0 commit comments

Comments
 (0)