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 else
enum isDefaultPred = false; enum isDefaultPred = false;
// Special case for two arrays static if (isDefaultPred && isArray!R1 && isArray!R2 &&
static if (isArray!R1 && isArray!R2 && is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
((!isSomeString!R1 && !isSomeString!R2) || {
(isSomeString!R1 && isSomeString!R2 && if (haystack.length < needle.length) return false;
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))) &&
isDefaultPred))) return haystack[0 .. needle.length] == needle;
}
else static if (isArray!R1 && isArray!R2 &&
!isNarrowString!R1 && !isNarrowString!R2)
{ {
if (haystack.length < needle.length) return false; if (haystack.length < needle.length) return false;
@ -4205,8 +4208,7 @@ unittest
debug(std_algorithm) scope(success) debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done."); writeln("unittest @", __FILE__, ":", __LINE__, " done.");
//foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (S; TypeTuple!(char[], wstring))
{ {
assert(!startsWith(to!S("abc"), 'c')); assert(!startsWith(to!S("abc"), 'c'));
assert(startsWith(to!S("abc"), 'a', 'c') == 1); 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("abc"), 'x', 'n', 'a') == 3);
assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2); assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
//foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (T; TypeTuple!(dchar[], string))
{ {
assert(startsWith(to!S("abc"), to!T(""))); assert(startsWith(to!S("abc"), to!T("")));
assert(startsWith(to!S("ab"), to!T("a"))); assert(startsWith(to!S("ab"), to!T("a")));
@ -4238,28 +4239,33 @@ unittest
} }
} }
assert(startsWith([0, 1, 2, 3, 4, 5], cast(int[])null)); foreach(T; TypeTuple!(int, short))
assert(!startsWith([0, 1, 2, 3, 4, 5], 5)); {
assert(!startsWith([0, 1, 2, 3, 4, 5], 1)); immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
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);
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 1)); assert(startsWith(arr, cast(int[])null));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 0)); assert(!startsWith(arr, 5));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0])); assert(!startsWith(arr, 1));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1])); assert(startsWith(arr, 0));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1], 7) == 1); assert(startsWith(arr, 5, 0, 1) == 2);
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7])); assert(startsWith(arr, [0]));
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7], [0, 1, 2]) == 2); assert(startsWith(arr, [0, 1]));
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1]))); assert(startsWith(arr, [0, 1], 7) == 1);
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1]), 7) == 1); assert(!startsWith(arr, [0, 1, 7]));
assert(!startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1, 7]))); assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2);
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], filter!"true"([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 else
enum isDefaultPred = false; enum isDefaultPred = false;
// Special case for two arrays static if (isDefaultPred && isArray!R1 && isArray!R2 &&
static if (isArray!R1 && isArray!R2 && is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
((!isSomeString!R1 && !isSomeString!R2) || {
(isSomeString!R1 && isSomeString!R2 && if (haystack.length < needle.length) return false;
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))) &&
isDefaultPred))) return haystack[$ - needle.length .. $] == needle;
}
else static if (isArray!R1 && isArray!R2 &&
!isNarrowString!R1 && !isNarrowString!R2)
{ {
if (haystack.length < needle.length) return false; if (haystack.length < needle.length) return false;
immutable diff = haystack.length - needle.length; immutable diff = haystack.length - needle.length;
foreach (j; 0 .. 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 // not found
return false; return false;
} }
@ -4523,8 +4532,7 @@ unittest
return Result(r); return Result(r);
} }
//foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (S; TypeTuple!(char[], wstring))
{ {
assert(!endsWith(to!S("abc"), 'a')); assert(!endsWith(to!S("abc"), 'a'));
assert(endsWith(to!S("abc"), 'a', 'c') == 2); 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"), 'x', 'n', 'c') == 3);
assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2); assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
//foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
foreach (T; TypeTuple!(dchar[], string))
{ {
assert(endsWith(to!S("abc"), to!T(""))); assert(endsWith(to!S("abc"), to!T("")));
assert(!endsWith(to!S("abc"), to!T("a"))); assert(!endsWith(to!S("abc"), to!T("a")));
@ -4551,28 +4558,33 @@ unittest
} }
} }
assert(endsWith([0, 1, 2, 3, 4, 5], cast(int[])null)); foreach(T; TypeTuple!(int, short))
assert(!endsWith([0, 1, 2, 3, 4, 5], 0)); {
assert(!endsWith([0, 1, 2, 3, 4, 5], 4)); immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
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);
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), 4)); assert(endsWith(arr, cast(int[])null));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), 5)); assert(!endsWith(arr, 0));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [5])); assert(!endsWith(arr, 4));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5])); assert(endsWith(arr, 5));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5], 7) == 1); assert(endsWith(arr, 0, 4, 5) == 3);
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5])); assert(endsWith(arr, [5]));
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5], [3, 4, 5]) == 2); assert(endsWith(arr, [4, 5]));
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5]))); assert(endsWith(arr, [4, 5], 7) == 1);
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5]), 7) == 1); assert(!endsWith(arr, [2, 4, 5]));
assert(!endsWith([0, 1, 2, 3, 4, 5], wrap([2, 4, 5]))); assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2);
assert(endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5], wrap([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,28 +1470,49 @@ deprecated String stripl(String)(String s)
/++ /++
Strips leading whitespace. 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 C[] stripLeft(C)(C[] str) @safe pure
if(isSomeString!S) if(isSomeChar!C)
{ {
bool foundIt; foreach(i, dchar c; str)
size_t nonWhite;
foreach(i, dchar c; s)
{ {
if(!std.uni.isWhite(c)) if(!std.uni.isWhite(c))
{ return str[i .. $];
foundIt = true;
nonWhite = i;
break;
}
} }
if(foundIt) return str[0 .. 0]; //Empty string with correct type.
return s[nonWhite .. $];
return s[0 .. 0]; //Empty string with correct type.
} }
//Verify Example.
unittest
{
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]);
}
/***************************************** /*****************************************
* $(RED Deprecated. It will be removed in August 2012. * $(RED Deprecated. It will be removed in August 2012.
* Please use $(D stripRight) instead.) * Please use $(D stripRight) instead.)
@ -1505,101 +1526,226 @@ deprecated String stripr(String)(String s)
/++ /++
Strips trailing whitespace. 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) C[] stripRight(C)(C[] str)
if(isSomeString!S) if(isSomeChar!C)
{ {
alias typeof(s[0]) C; foreach_reverse(i, dchar c; str)
size_t codeLen;
foreach(dchar c; retro(s))
{ {
if(std.uni.isWhite(c)) if(!std.uni.isWhite(c))
codeLen += codeLength!C(c); return str[0 .. i + codeLength!C(c)];
else
break;
} }
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. 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) C[] strip(C)(C[] str)
if(isSomeString!S) 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 unittest
{ {
debug(string) printf("string.strip.unittest\n"); debug(string) printf("string.strip.unittest\n");
assert(stripLeft(" foo\t ") == "foo\t "); foreach(S; TypeTuple!(char[], const char[], string,
assert(stripLeft("\u2008 foo\t \u2007") == "foo\t \u2007"); wchar[], const wchar[], wstring,
assert(stripLeft("1") == "1"); 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(equal(stripRight(to!S(" foo\t ")), " foo"));
assert(stripRight("\u2008 foo\t \u2007") == "\u2008 foo"); assert(equal(stripRight(to!S("\u2008 foo\t \u2007")), "\u2008 foo"));
assert(stripRight("1") == "1"); 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(equal(strip(to!S(" foo\t ")), "foo"));
assert(strip("\u2008 foo\t \u2007") == "foo"); assert(equal(strip(to!S("\u2008 foo\t \u2007")), "foo"));
assert(strip("1") == "1"); 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) If $(D str) ends with $(D delimiter), then $(D str) is returned without
is given, then one trailing $(D '\r'), $(D '\n'), $(D "\r\n"), $(D delimiter) on its end. If it $(D str) does $(I not) end with
$(XREF uni, lineSep), or $(XREF uni, paraSep) is removed. $(D delimiter), then it is returned unchanged.
+/
S chomp(S)(S s)
if(isSomeString!S)
{
if(s.empty)
return s;
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': case '\n':
{ {
s.popBack(); if(str.length > 1 && str[$ - 2] == '\r')
return str[0 .. $ - 2];
if(!s.empty && s.back == '\r') goto case;
s.popBack();
break;
} }
case '\r': case '\r':
case lineSep: return str[0 .. $ - 1];
case paraSep:
//Pops off the last character if it's lineSep or paraSep.
static if(is(C : const char))
{ {
s.popBack(); //In UTF-8, lineSep and paraSep are [226, 128, 168], and
break; //[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:
return str[0 .. $ - 1];
} }
default: default:
break; return str;
} }
return s;
} }
/// Ditto /// Ditto
S chomp(S, C)(S s, const(C)[] delimiter) C1[] chomp(C1, C2)(C1[] str, const(C2)[] delimiter)
if(isSomeString!S && isSomeString!(C[])) if(isSomeChar!C1 && isSomeChar!C2)
{ {
if(delimiter.empty) if(delimiter.empty)
return chomp(s); return chomp(str);
else if(endsWith(s, delimiter))
static if(is(Unqual!C1 == Unqual!C2))
{ {
static if(is(Unqual!(typeof(s[0])) == Unqual!C)) if(str.endsWith(delimiter))
return s[0 .. $ - delimiter.length]; return str[0 .. $ - delimiter.length];
else
return s[0 .. $ - to!S(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 unittest
@ -1628,7 +1774,7 @@ unittest
{ {
// @@@ BUG IN COMPILER, MUST INSERT CAST // @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S)null, cast(T)null) is null); 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("o")) == "hell");
assert(chomp(to!S("hello"), to!T("p")) == "hello"); assert(chomp(to!S("hello"), to!T("p")) == "hello");
// @@@ BUG IN COMPILER, MUST INSERT CAST // @@@ BUG IN COMPILER, MUST INSERT CAST
@ -1642,47 +1788,123 @@ unittest
/++ /++
If $(D longer.startsWith(shorter)), returns $(D longer[shorter.length .. $]). If $(D str) starts with $(D delimiter), then the part of $(D str) following
Otherwise, returns $(D longer). $(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) C1[] chompPrefix(C1, C2)(C1[] str, C2[] delimiter)
if(isSomeString!(C1[]) && isSomeString!(C2[])) 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 unittest
{ {
assert(chompPrefix("abcdefgh", "abcde") == "fgh"); foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
assert(chompPrefix("abcde", "abcdefgh") == "abcde"); {
assert(chompPrefix("\uFF28el\uFF4co", "\uFF28el\uFF4co") == ""); foreach(T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
assert(chompPrefix("\uFF28el\uFF4co", "\uFF28el") == "\uFF4co"); {
assert(chompPrefix("\uFF28el", "\uFF28el\uFF4co") == "\uFF28el"); 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. Returns $(D str) without its last character, if there is one. If $(D str)
If $(D s) ends in "\r\n", then both are removed. 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(str.empty)
if (!len) return s; return str;
if (len >= 2 && s[len - 1] == '\n' && s[len - 2] == '\r')
return s[0 .. len - 2]; if(str.length >= 2 && str[$ - 1] == '\n' && str[$ - 2] == '\r')
s.popBack(); return str[0 .. $ - 2];
return s;
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 unittest
{ {
debug(string) printf("string.chop.unittest\n"); debug(string) printf("string.chop.unittest\n");
assert(chop(cast(string) null) is null); foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
assert(chop("hello") == "hell"); {
assert(chop("hello\r\n") == "hello"); assert(chop(cast(S) null) is null);
assert(chop("hello\n\r") == "hello\n"); 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(`ミツバチと科学者`)), "ミツバチと科学"));
}
} }