Merge pull request #655 from jmdavis/string

Fix for issue# 8323.
This commit is contained in:
Andrei Alexandrescu 2012-07-08 20:16:36 -07:00
commit 8c747b0aba
2 changed files with 389 additions and 155 deletions

View file

@ -4152,12 +4152,15 @@ if (isInputRange!R1 &&
else
enum isDefaultPred = false;
// Special case for two arrays
static if (isArray!R1 && isArray!R2 &&
((!isSomeString!R1 && !isSomeString!R2) ||
(isSomeString!R1 && isSomeString!R2 &&
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))) &&
isDefaultPred)))
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
{
if (haystack.length < needle.length) return false;
return haystack[0 .. needle.length] == needle;
}
else static if (isArray!R1 && isArray!R2 &&
!isNarrowString!R1 && !isNarrowString!R2)
{
if (haystack.length < needle.length) return false;
@ -4205,8 +4208,7 @@ unittest
debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
//foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (S; TypeTuple!(char[], wstring))
foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(!startsWith(to!S("abc"), 'c'));
assert(startsWith(to!S("abc"), 'a', 'c') == 1);
@ -4214,8 +4216,7 @@ unittest
assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3);
assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
//foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (T; TypeTuple!(dchar[], string))
foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(startsWith(to!S("abc"), to!T("")));
assert(startsWith(to!S("ab"), to!T("a")));
@ -4238,28 +4239,33 @@ unittest
}
}
assert(startsWith([0, 1, 2, 3, 4, 5], cast(int[])null));
assert(!startsWith([0, 1, 2, 3, 4, 5], 5));
assert(!startsWith([0, 1, 2, 3, 4, 5], 1));
assert(startsWith([0, 1, 2, 3, 4, 5], 0));
assert(startsWith([0, 1, 2, 3, 4, 5], 5, 0, 1) == 2);
assert(startsWith([0, 1, 2, 3, 4, 5], [0]));
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1]));
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1], 7) == 1);
assert(!startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7]));
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], [0, 1, 2]) == 2);
foreach(T; TypeTuple!(int, short))
{
immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 1));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 0));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0]));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1]));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1], 7) == 1);
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7]));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7], [0, 1, 2]) == 2);
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1])));
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1]), 7) == 1);
assert(!startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1, 7])));
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], filter!"true"([0, 1, 2])) == 2);
assert(startsWith(arr, cast(int[])null));
assert(!startsWith(arr, 5));
assert(!startsWith(arr, 1));
assert(startsWith(arr, 0));
assert(startsWith(arr, 5, 0, 1) == 2);
assert(startsWith(arr, [0]));
assert(startsWith(arr, [0, 1]));
assert(startsWith(arr, [0, 1], 7) == 1);
assert(!startsWith(arr, [0, 1, 7]));
assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2);
assert(!startsWith(filter!"true"(arr), 1));
assert(startsWith(filter!"true"(arr), 0));
assert(startsWith(filter!"true"(arr), [0]));
assert(startsWith(filter!"true"(arr), [0, 1]));
assert(startsWith(filter!"true"(arr), [0, 1], 7) == 1);
assert(!startsWith(filter!"true"(arr), [0, 1, 7]));
assert(startsWith(filter!"true"(arr), [0, 1, 7], [0, 1, 2]) == 2);
assert(startsWith(arr, filter!"true"([0, 1])));
assert(startsWith(arr, filter!"true"([0, 1]), 7) == 1);
assert(!startsWith(arr, filter!"true"([0, 1, 7])));
assert(startsWith(arr, [0, 1, 7], filter!"true"([0, 1, 2])) == 2);
}
}
/**
@ -4451,18 +4457,21 @@ if (isInputRange!R1 &&
else
enum isDefaultPred = false;
// Special case for two arrays
static if (isArray!R1 && isArray!R2 &&
((!isSomeString!R1 && !isSomeString!R2) ||
(isSomeString!R1 && isSomeString!R2 &&
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))) &&
isDefaultPred)))
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
{
if (haystack.length < needle.length) return false;
return haystack[$ - needle.length .. $] == needle;
}
else static if (isArray!R1 && isArray!R2 &&
!isNarrowString!R1 && !isNarrowString!R2)
{
if (haystack.length < needle.length) return false;
immutable diff = haystack.length - needle.length;
foreach (j; 0 .. needle.length)
{
if (!binaryFun!(pred)(needle[j], haystack[j + diff]))
if (!binaryFun!pred(needle[j], haystack[j + diff]))
// not found
return false;
}
@ -4523,8 +4532,7 @@ unittest
return Result(r);
}
//foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (S; TypeTuple!(char[], wstring))
foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(!endsWith(to!S("abc"), 'a'));
assert(endsWith(to!S("abc"), 'a', 'c') == 2);
@ -4532,8 +4540,7 @@ unittest
assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3);
assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
//foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (T; TypeTuple!(dchar[], string))
foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(endsWith(to!S("abc"), to!T("")));
assert(!endsWith(to!S("abc"), to!T("a")));
@ -4551,28 +4558,33 @@ unittest
}
}
assert(endsWith([0, 1, 2, 3, 4, 5], cast(int[])null));
assert(!endsWith([0, 1, 2, 3, 4, 5], 0));
assert(!endsWith([0, 1, 2, 3, 4, 5], 4));
assert(endsWith([0, 1, 2, 3, 4, 5], 5));
assert(endsWith([0, 1, 2, 3, 4, 5], 0, 4, 5) == 3);
assert(endsWith([0, 1, 2, 3, 4, 5], [5]));
assert(endsWith([0, 1, 2, 3, 4, 5], [4, 5]));
assert(endsWith([0, 1, 2, 3, 4, 5], [4, 5], 7) == 1);
assert(!endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5]));
assert(endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5], [3, 4, 5]) == 2);
foreach(T; TypeTuple!(int, short))
{
immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), 4));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), 5));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [5]));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5]));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5], 7) == 1);
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5]));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5], [3, 4, 5]) == 2);
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5])));
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5]), 7) == 1);
assert(!endsWith([0, 1, 2, 3, 4, 5], wrap([2, 4, 5])));
assert(endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5], wrap([3, 4, 5])) == 2);
assert(endsWith(arr, cast(int[])null));
assert(!endsWith(arr, 0));
assert(!endsWith(arr, 4));
assert(endsWith(arr, 5));
assert(endsWith(arr, 0, 4, 5) == 3);
assert(endsWith(arr, [5]));
assert(endsWith(arr, [4, 5]));
assert(endsWith(arr, [4, 5], 7) == 1);
assert(!endsWith(arr, [2, 4, 5]));
assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2);
assert(!endsWith(wrap(arr), 4));
assert(endsWith(wrap(arr), 5));
assert(endsWith(wrap(arr), [5]));
assert(endsWith(wrap(arr), [4, 5]));
assert(endsWith(wrap(arr), [4, 5], 7) == 1);
assert(!endsWith(wrap(arr), [2, 4, 5]));
assert(endsWith(wrap(arr), [2, 4, 5], [3, 4, 5]) == 2);
assert(endsWith(arr, wrap([4, 5])));
assert(endsWith(arr, wrap([4, 5]), 7) == 1);
assert(!endsWith(arr, wrap([2, 4, 5])));
assert(endsWith(arr, [2, 4, 5], wrap([3, 4, 5])) == 2);
}
}
/**

View file

@ -1470,27 +1470,48 @@ deprecated String stripl(String)(String s)
/++
Strips leading whitespace.
Examples:
--------------------
assert(stripLeft(" hello world ") ==
"hello world ");
assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
"hello world\n\t\v\r");
assert(stripLeft("hello world") ==
"hello world");
assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
"hello world" ~ [lineSep]);
assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) ==
"hello world" ~ [paraSep]);
--------------------
+/
S stripLeft(S)(S s) @safe pure
if(isSomeString!S)
C[] stripLeft(C)(C[] str) @safe pure
if(isSomeChar!C)
{
bool foundIt;
size_t nonWhite;
foreach(i, dchar c; s)
foreach(i, dchar c; str)
{
if(!std.uni.isWhite(c))
return str[i .. $];
}
return str[0 .. 0]; //Empty string with correct type.
}
//Verify Example.
unittest
{
foundIt = true;
nonWhite = i;
break;
}
assert(stripLeft(" hello world ") ==
"hello world ");
assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
"hello world\n\t\v\r");
assert(stripLeft("hello world") ==
"hello world");
assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
"hello world" ~ [lineSep]);
assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) ==
"hello world" ~ [paraSep]);
}
if(foundIt)
return s[nonWhite .. $];
return s[0 .. 0]; //Empty string with correct type.
}
/*****************************************
* $(RED Deprecated. It will be removed in August 2012.
@ -1505,101 +1526,226 @@ deprecated String stripr(String)(String s)
/++
Strips trailing whitespace.
Examples:
--------------------
assert(stripRight(" hello world ") ==
" hello world");
assert(stripRight("\n\t\v\rhello world\n\t\v\r") ==
"\n\t\v\rhello world");
assert(stripRight("hello world") ==
"hello world");
assert(stripRight([lineSep] ~ "hello world" ~ lineSep) ==
[lineSep] ~ "hello world");
assert(stripRight([paraSep] ~ "hello world" ~ paraSep) ==
[paraSep] ~ "hello world");
--------------------
+/
S stripRight(S)(S s)
if(isSomeString!S)
C[] stripRight(C)(C[] str)
if(isSomeChar!C)
{
alias typeof(s[0]) C;
size_t codeLen;
foreach(dchar c; retro(s))
foreach_reverse(i, dchar c; str)
{
if(std.uni.isWhite(c))
codeLen += codeLength!C(c);
else
break;
if(!std.uni.isWhite(c))
return str[0 .. i + codeLength!C(c)];
}
return s[0 .. $ - codeLen];
return str[0 .. 0];
}
//Verify Example.
unittest
{
assert(stripRight(" hello world ") ==
" hello world");
assert(stripRight("\n\t\v\rhello world\n\t\v\r") ==
"\n\t\v\rhello world");
assert(stripRight("hello world") ==
"hello world");
assert(stripRight([lineSep] ~ "hello world" ~ lineSep) ==
[lineSep] ~ "hello world");
assert(stripRight([paraSep] ~ "hello world" ~ paraSep) ==
[paraSep] ~ "hello world");
}
/++
Strips both leading and trailing whitespace.
Examples:
--------------------
assert(strip(" hello world ") ==
"hello world");
assert(strip("\n\t\v\rhello world\n\t\v\r") ==
"hello world");
assert(strip("hello world") ==
"hello world");
assert(strip([lineSep] ~ "hello world" ~ [lineSep]) ==
"hello world");
assert(strip([paraSep] ~ "hello world" ~ [paraSep]) ==
"hello world");
--------------------
+/
S strip(S)(S s)
if(isSomeString!S)
C[] strip(C)(C[] str)
if(isSomeChar!C)
{
return stripRight(stripLeft(s));
return stripRight(stripLeft(str));
}
//Verify Example.
unittest
{
assert(strip(" hello world ") ==
"hello world");
assert(strip("\n\t\v\rhello world\n\t\v\r") ==
"hello world");
assert(strip("hello world") ==
"hello world");
assert(strip([lineSep] ~ "hello world" ~ [lineSep]) ==
"hello world");
assert(strip([paraSep] ~ "hello world" ~ [paraSep]) ==
"hello world");
}
unittest
{
debug(string) printf("string.strip.unittest\n");
assert(stripLeft(" foo\t ") == "foo\t ");
assert(stripLeft("\u2008 foo\t \u2007") == "foo\t \u2007");
assert(stripLeft("1") == "1");
foreach(S; TypeTuple!(char[], const char[], string,
wchar[], const wchar[], wstring,
dchar[], const dchar[], dstring))
{
assert(equal(stripLeft(to!S(" foo\t ")), "foo\t "));
assert(equal(stripLeft(to!S("\u2008 foo\t \u2007")), "foo\t \u2007"));
assert(equal(stripLeft(to!S("\u0085 μ \u0085 \u00BB \r")), "μ \u0085 \u00BB \r"));
assert(equal(stripLeft(to!S("1")), "1"));
assert(equal(stripLeft(to!S("\U0010FFFE")), "\U0010FFFE"));
assert(equal(stripLeft(to!S("")), ""));
assert(stripRight(" foo\t ") == " foo");
assert(stripRight("\u2008 foo\t \u2007") == "\u2008 foo");
assert(stripRight("1") == "1");
assert(equal(stripRight(to!S(" foo\t ")), " foo"));
assert(equal(stripRight(to!S("\u2008 foo\t \u2007")), "\u2008 foo"));
assert(equal(stripRight(to!S("\u0085 μ \u0085 \u00BB \r")), "\u0085 μ \u0085 \u00BB"));
assert(equal(stripRight(to!S("1")), "1"));
assert(equal(stripRight(to!S("\U0010FFFE")), "\U0010FFFE"));
assert(equal(stripRight(to!S("")), ""));
assert(strip(" foo\t ") == "foo");
assert(strip("\u2008 foo\t \u2007") == "foo");
assert(strip("1") == "1");
assert(equal(strip(to!S(" foo\t ")), "foo"));
assert(equal(strip(to!S("\u2008 foo\t \u2007")), "foo"));
assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB \r")), "μ \u0085 \u00BB"));
assert(equal(strip(to!S("\U0010FFFE")), "\U0010FFFE"));
assert(equal(strip(to!S("")), ""));
}
}
/++
Returns $(D s) sans the trailing $(D delimiter), if any. If no $(D delimiter)
is given, then one trailing $(D '\r'), $(D '\n'), $(D "\r\n"),
$(XREF uni, lineSep), or $(XREF uni, paraSep) is removed.
+/
S chomp(S)(S s)
if(isSomeString!S)
{
if(s.empty)
return s;
If $(D str) ends with $(D delimiter), then $(D str) is returned without
$(D delimiter) on its end. If it $(D str) does $(I not) end with
$(D delimiter), then it is returned unchanged.
switch(s.back)
If no $(D delimiter) is given, then one trailing $(D '\r'), $(D '\n'),
$(D "\r\n"), $(XREF uni, lineSep), or $(XREF uni, paraSep) is removed from
the end of $(D str). If $(D str) does not end with any of those characters,
then it is returned unchanged.
Examples:
--------------------
assert(chomp(" hello world \n\r") == " hello world \n");
assert(chomp(" hello world \r\n") == " hello world ");
assert(chomp(" hello world \n\n") == " hello world \n");
assert(chomp(" hello world \n\n ") == " hello world \n\n ");
assert(chomp(" hello world \n\n" ~ [lineSep]) == " hello world \n\n");
assert(chomp(" hello world \n\n" ~ [paraSep]) == " hello world \n\n");
assert(chomp(" hello world") == " hello world");
assert(chomp("") == "");
assert(chomp(" hello world", "orld") == " hello w");
assert(chomp(" hello world", " he") == " hello world");
assert(chomp("", "hello") == "");
--------------------
+/
C[] chomp(C)(C[] str)
if(isSomeChar!C)
{
if(str.empty)
return str;
switch(str[$ - 1])
{
case '\n':
{
s.popBack();
if(!s.empty && s.back == '\r')
s.popBack();
break;
if(str.length > 1 && str[$ - 2] == '\r')
return str[0 .. $ - 2];
goto case;
}
case '\r':
return str[0 .. $ - 1];
//Pops off the last character if it's lineSep or paraSep.
static if(is(C : const char))
{
//In UTF-8, lineSep and paraSep are [226, 128, 168], and
//[226, 128, 169] respectively, so their first two bytes are the same.
case 168: //Last byte of lineSep
case 169: //Last byte of paraSep
{
if(str.length > 2 && str[$ - 2] == 128 && str[$ - 3] == 226)
return str [0 .. $ - 3];
goto default;
}
}
else
{
case lineSep:
case paraSep:
{
s.popBack();
break;
return str[0 .. $ - 1];
}
default:
break;
return str;
}
return s;
}
/// Ditto
S chomp(S, C)(S s, const(C)[] delimiter)
if(isSomeString!S && isSomeString!(C[]))
C1[] chomp(C1, C2)(C1[] str, const(C2)[] delimiter)
if(isSomeChar!C1 && isSomeChar!C2)
{
if(delimiter.empty)
return chomp(s);
else if(endsWith(s, delimiter))
return chomp(str);
static if(is(Unqual!C1 == Unqual!C2))
{
static if(is(Unqual!(typeof(s[0])) == Unqual!C))
return s[0 .. $ - delimiter.length];
else
return s[0 .. $ - to!S(delimiter).length];
if(str.endsWith(delimiter))
return str[0 .. $ - delimiter.length];
}
return s;
auto orig = str;
foreach_reverse(dchar c; delimiter)
{
if(str.empty || str.back != c)
return orig;
str.popBack();
}
return str;
}
//Verify Example.
unittest
{
assert(chomp(" hello world \n\r") == " hello world \n");
assert(chomp(" hello world \r\n") == " hello world ");
assert(chomp(" hello world \n\n") == " hello world \n");
assert(chomp(" hello world \n\n ") == " hello world \n\n ");
assert(chomp(" hello world \n\n" ~ [lineSep]) == " hello world \n\n");
assert(chomp(" hello world \n\n" ~ [paraSep]) == " hello world \n\n");
assert(chomp(" hello world") == " hello world");
assert(chomp("") == "");
assert(chomp(" hello world", "orld") == " hello w");
assert(chomp(" hello world", " he") == " hello world");
assert(chomp("", "hello") == "");
}
unittest
@ -1628,7 +1774,7 @@ unittest
{
// @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S)null, cast(T)null) is null);
assert(chomp("hello\n", cast(T)null) == "hello");
assert(chomp(to!S("hello\n"), cast(T)null) == "hello");
assert(chomp(to!S("hello"), to!T("o")) == "hell");
assert(chomp(to!S("hello"), to!T("p")) == "hello");
// @@@ BUG IN COMPILER, MUST INSERT CAST
@ -1642,47 +1788,123 @@ unittest
/++
If $(D longer.startsWith(shorter)), returns $(D longer[shorter.length .. $]).
Otherwise, returns $(D longer).
If $(D str) starts with $(D delimiter), then the part of $(D str) following
$(D delimiter) is returned. If it $(D str) does $(I not) start with
$(D delimiter), then it is returned unchanged.
Examples:
--------------------
assert(chompPrefix("hello world", "he") == "llo world");
assert(chompPrefix("hello world", "hello w") == "orld");
assert(chompPrefix("hello world", " world") == "hello world");
assert(chompPrefix("", "hello") == "");
--------------------
+/
C1[] chompPrefix(C1, C2)(C1[] longer, C2[] shorter)
if(isSomeString!(C1[]) && isSomeString!(C2[]))
C1[] chompPrefix(C1, C2)(C1[] str, C2[] delimiter)
if(isSomeChar!C1 && isSomeChar!C2)
{
return startsWith(longer, shorter) ? longer[shorter.length .. $] : longer;
static if(is(Unqual!C1 == Unqual!C2))
{
if(str.startsWith(delimiter))
return str[delimiter.length .. $];
return str;
}
else
{
auto orig = str;
size_t index = 0;
foreach(dchar c; delimiter)
{
if(index >= str.length || decode(str, index) != c)
return orig;
}
return str[index .. $];
}
}
//Verify Example.
unittest
{
assert(chompPrefix("hello world", "he") == "llo world");
assert(chompPrefix("hello world", "hello w") == "orld");
assert(chompPrefix("hello world", " world") == "hello world");
assert(chompPrefix("", "hello") == "");
}
unittest
{
assert(chompPrefix("abcdefgh", "abcde") == "fgh");
assert(chompPrefix("abcde", "abcdefgh") == "abcde");
assert(chompPrefix("\uFF28el\uFF4co", "\uFF28el\uFF4co") == "");
assert(chompPrefix("\uFF28el\uFF4co", "\uFF28el") == "\uFF4co");
assert(chompPrefix("\uFF28el", "\uFF28el\uFF4co") == "\uFF28el");
foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
foreach(T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(equal(chompPrefix(to!S("abcdefgh"), to!T("abcde")), "fgh"));
assert(equal(chompPrefix(to!S("abcde"), to!T("abcdefgh")), "abcde"));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el\uFF4co")), ""));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")), "\uFF4co"));
assert(equal(chompPrefix(to!S("\uFF28el"), to!T("\uFF28el\uFF4co")), "\uFF28el"));
}
}
}
/++
Returns $(D s) sans its last character, if there is one.
If $(D s) ends in "\r\n", then both are removed.
Returns $(D str) without its last character, if there is one. If $(D str)
ends with $(D "\r\n"), then both are removed. If $(D str) is empty, then
then it is returned unchanged.
Examples:
--------------------
assert(chop("hello world") == "hello worl");
assert(chop("hello world\n") == "hello world");
assert(chop("hello world\r") == "hello world");
assert(chop("hello world\n\r") == "hello world\n");
assert(chop("hello world\r\n") == "hello world");
assert(chop("Walter Bright") == "Walter Brigh");
assert(chop("") == "");
--------------------
+/
S chop(S)(S s) if (isSomeString!S)
S chop(S)(S str)
if(isSomeString!S)
{
auto len = s.length;
if (!len) return s;
if (len >= 2 && s[len - 1] == '\n' && s[len - 2] == '\r')
return s[0 .. len - 2];
s.popBack();
return s;
if(str.empty)
return str;
if(str.length >= 2 && str[$ - 1] == '\n' && str[$ - 2] == '\r')
return str[0 .. $ - 2];
str.popBack();
return str;
}
//Verify Example.
unittest
{
assert(chop("hello world") == "hello worl");
assert(chop("hello world\n") == "hello world");
assert(chop("hello world\r") == "hello world");
assert(chop("hello world\n\r") == "hello world\n");
assert(chop("hello world\r\n") == "hello world");
assert(chop("Walter Bright") == "Walter Brigh");
assert(chop("") == "");
}
unittest
{
debug(string) printf("string.chop.unittest\n");
assert(chop(cast(string) null) is null);
assert(chop("hello") == "hell");
assert(chop("hello\r\n") == "hello");
assert(chop("hello\n\r") == "hello\n");
foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(chop(cast(S) null) is null);
assert(equal(chop(to!S("hello")), "hell"));
assert(equal(chop(to!S("hello\r\n")), "hello"));
assert(equal(chop(to!S("hello\n\r")), "hello\n"));
assert(equal(chop(to!S("Verité")), "Verit"));
assert(equal(chop(to!S(`さいごの果実`)), "さいごの果"));
assert(equal(chop(to!S(`ミツバチと科学者`)), "ミツバチと科学"));
}
}