Mercurial > projects > dynamin
diff dynamin/core/string.d @ 106:acdbb30fee7e
Port to D2.
Most of the effort was dealing with immutable and const.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 17 Dec 2012 23:41:50 -0600 |
parents | 73060bc3f004 |
children | 6613b65a6035 |
line wrap: on
line diff
--- a/dynamin/core/string.d Sat Nov 24 10:21:50 2012 -0600 +++ b/dynamin/core/string.d Mon Dec 17 23:41:50 2012 -0600 @@ -24,22 +24,24 @@ import dynamin.core.global; import dynamin.core.math; -/// Defined as a char[] -alias char[] string; +/// Defined as char[] +alias char[] mstring; +/// Defined as const(char)[] +alias const(char)[] cstring; /// -char* toCharPtr(char[] str) { - return (str~'\0').ptr; +char* toCharPtr(cstring str) { + return (str~'\0').dup.ptr; } /// -wchar* toWcharPtr(char[] str) { +wchar* toWcharPtr(cstring str) { return toString16(str~'\0').ptr; } /* -string ToString(ulong num, uint base = 10) { +string toString(ulong num, uint base = 10) { if(base > 16) - throw new Exception("ToString() - radix more than 16"); + throw new Exception("toString() - radix more than 16"); char[] digits = "0123456789abcdef"; string str; ulong div = base; @@ -76,15 +78,21 @@ static this() { formatter = new Layout!(char); } -string format(char[] str, ...) { - return formatter.convert(_arguments, _argptr, str); +string format(cstring str, ...) { + return formatter.convert(_arguments, _argptr, str).idup; } unittest { assert(format("I am {}", 20) == "I am 20"); } /** - * Converts all lowercase characters in the specified string to uppercase. + * Converts all lowercase characters in the specified string to uppercase. Obviously, the + * conversion is not done in place, but in-place conversion can be accomplished by passing + * the same string as `buffer`. + * + * Do not use this function to normalize strings to the same case (like to compare them). Instead, + * use toUppercase(). See http://msdn.microsoft.com/en-us/library/bb386042%28v=vs.90%29.aspx. + * * Examples: * ----- * "Bounce the ball.".upcase() == "BOUNCE THE BALL." @@ -92,16 +100,21 @@ * "æóëø".upcase() == "ÆÓËØ" * ----- */ -string upcase(string str) { +mstring upcase(cstring str, mstring buffer = null) { // TODO: use buffer return toUpper(str); } unittest { assert("Bounce the ball.".upcase() == "BOUNCE THE BALL."); assert("Mañana".upcase() == "MAÑANA"); assert("æóëø".upcase() == "ÆÓËØ"); + assert("σε".downcase() == "ΣΕ"); } + /** - * Converts all uppercase characters in the specified string to lowercase. + * Converts all uppercase characters in the specified string to lowercase. Obviously, the + * conversion is not done in place, but in-place conversion can be accomplished by passing + * the same string as `buffer`. + * * Examples: * ----- * "BoUnCe ThE BALL.".downcase() == "bounce the ball." @@ -109,80 +122,87 @@ * "ÆÓËØ".downcase() == "æóëø" * ----- */ -string downcase(string str) { +mstring downcase(cstring str, mstring buffer = null) { // TODO: use buffer return toLower(str); } unittest { assert("BoUnCe ThE BALL.".downcase() == "bounce the ball."); assert("MAÑANA".downcase() == "mañana"); assert("ÆÓËØ".downcase() == "æóëø"); + assert("ΣΕ".downcase() == "σε"); } // TODO: make more use of delegates in these? // TODO; use templates so that these work with wchar and dchar? -bool startsWith(string str, string subStr, int start = 0) { +bool startsWith(cstring str, cstring subStr, int start = 0) { if(start+subStr.length > str.length) return false; return str[start..start+subStr.length] == subStr; } -bool endsWith(string str, string subStr) { +bool endsWith(cstring str, cstring subStr) { return endsWith(str, subStr, str.length); } -bool endsWith(string str, string subStr, int start) { +bool endsWith(cstring str, cstring subStr, int start) { if(start-subStr.length < 0) return false; return str[str.length-subStr.length..str.length] == subStr; } -int findLast(string str, string subStr) { +int findLast(cstring str, cstring subStr) { return findLast(str, subStr, str.length); } -int findLast(string str, string subStr, int start) { +int findLast(cstring str, cstring subStr, int start) { for(int i = start-subStr.length; i >= 0; --i) if(str[i..i+subStr.length] == subStr) return i; return -1; } -int find(string str, string subStr, int start = 0) { +int find(cstring str, cstring subStr, int start = 0) { for(int i = start; i < str.length-subStr.length; ++i) if(str[i..i+subStr.length] == subStr) return i; return -1; } -string remove(string str, int start, int count = 1) { - return str[0..start] ~ str[start+count..str.length]; +mstring remove(cstring str, int start, int count = 1, mstring buffer = null) { // TODO: use buffer + // can't use concatenation because const(char)[] ~ const(char)[] is const(char)[] + //return str[0..start] ~ str[start+count..str.length]; + + mstring str2 = new char[str.length - count]; + str2[0..start] = str[0..start]; + str2[start..start + count] = str[start+count..str.length]; + return str2; } // TODO: ? // split(string str, int delegate(string s) func) //string[] split(string str, string subStr) { // return split(str, (string s) { return s.startsWith(subStr) ? subStr.length, : -1; }; //} -// TODO: return slices to string + +/// Returns slices. //split1("50=20=10", "=") -> ["50", "20=10"] -string[] split1(string str, string subStr) { +inout(char)[][] split1(inout(char)[] str, cstring subStr) { if(subStr.length == 0) return [str]; int index = find(str, subStr); if(index == -1) return [str]; - string[] strs = new string[2]; - strs[0] = str[0..index].dup; - strs[1] = str[index+subStr.length..str.length].dup; + auto strs = new inout(char)[][2]; + strs[0] = str[0..index]; + strs[1] = str[index+subStr.length..str.length]; return strs; } -// TODO: return slices to string //split("50=20=10", "=") -> ["50", "20", "10"] -string[] split(string str, string subStr) { +inout(char)[][] split(inout(char)[] str, cstring subStr) { if(subStr.length == 0) return [str]; - string[] strs; + inout(char)[][] strs; int index, searchFrom; int i = 0; while(searchFrom < str.length) { index = find(str, subStr, searchFrom); if(index == -1) index = str.length; strs.length = strs.length+1; - strs[i] = str[searchFrom..index].dup; + strs[i] = str[searchFrom..index]; ++i; searchFrom = index+subStr.length; } @@ -212,9 +232,9 @@ * "\n\r\n".convertNewlines(Newline.Macintosh) == "\r\r" * ----- */ -string convertNewlines(string str, Newline nl) { +mstring convertNewlines(cstring str, Newline nl, mstring buffer = null) { // TODO: use buffer string lineSep; - switch(nl) { + final switch(nl) { case Newline.Cr: lineSep = "\r"; break; case Newline.Lf: lineSep = "\n"; break; case Newline.Crlf: lineSep = "\r\n"; break; @@ -237,15 +257,15 @@ * join(["aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html" * ----- */ -string join(string[] strs, string sep) { +mstring join(string[] strs, cstring sep) { if(strs.length == 0) - return ""; + return "".dup; int len; foreach(string s; strs) len += s.length; len += sep.length*(strs.length-1); - string newStr = new char[len]; + mstring newStr = new char[len]; newStr[0..strs[0].length] = strs[0]; int start = strs[0].length; for(int i = 1; i < strs.length; ++i) { @@ -258,10 +278,10 @@ return newStr; } unittest { - // TODO: remove cast(string) when D has bugs fixed + // TODO: remove cast(mstring) when D has bugs fixed assert(join(["10", "15", "17"], " - ") == "10 - 15 - 17"); assert(join(["789", "672", "484"], ",") == "789,672,484"); - assert(join([cast(string)"aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html"); + assert(join(["aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html"); } /** @@ -275,8 +295,8 @@ * "Hi".times(0) == "" * ----- */ -string times(string str, int n) { - string newStr = new char[n * str.length]; +mstring times(cstring str, int n) { + mstring newStr = new char[n * str.length]; for(int i = 0; i < newStr.length; i += str.length) newStr[i..i+str.length] = str; return newStr; @@ -290,22 +310,22 @@ // TODO: flesh out and make public struct sbuilder { - int Count; - string Data; - void Add(char c) { - if(Count + 1 > Data.length) - Data.length = (Data.length + 1) * 2; - Data[Count] = c; - ++Count; + int count; + mstring data; + void add(char c) { + if(count + 1 > data.length) + data.length = (data.length + 1) * 2; + data[count] = c; + ++count; } - void Add(string str) { - if(Count + str.length > Data.length) - Data.length = max((Data.length + 1) * 2, Count + str.length); - Data[Count..Count+str.length] = str; - Count += str.length; + void add(cstring str) { + if(count + str.length > data.length) + data.length = max((data.length + 1) * 2, count + str.length); + data[count..count+str.length] = str; + count += str.length; } - string ToString() { - return Data[0..Count].dup; + mstring toString() { + return data[0..count].dup; } } /** @@ -329,12 +349,12 @@ * ----- * Bug: If a search string has a length of zero, this method will go into an infinite loop. */ -string replace(string str, string[] searchStrs, string[] replacements) { +mstring replace(cstring str, string[] searchStrs, string[] replacements) { if(replacements.length == 1 && searchStrs.length > 1) { string tmp = replacements[0]; replacements = new string[searchStrs.length]; - foreach(i, dummy; searchStrs) - replacements[i] = tmp; + foreach(i, dummy; searchStrs) + replacements[i] = tmp; } if(searchStrs.length != replacements.length) throw new IllegalArgumentException( @@ -346,30 +366,30 @@ if(i+subStr.length <= str.length && str[i..i+subStr.length] == subStr) { // skip the part of string that matched i += subStr.length; - builder.Add(replacements[j]); + builder.add(replacements[j]); continue loop; } } - builder.Add(str[i]); + builder.add(str[i]); ++i; } - return builder.ToString(); + return builder.toString(); } /// ditto -string replace(string str, string[] searchStrs, string replacement) { +mstring replace(cstring str, string[] searchStrs, string replacement) { return str.replace(searchStrs, [replacement]); } /// ditto -string replace(string str, string searchStr, string replacement) { +mstring replace(cstring str, string searchStr, string replacement) { return str.replace([searchStr], [replacement]); } unittest { - assert("Mississippi".replace([cast(string)"is", "i"], [cast(string)"..", "*"]) == "M..s..s*pp*"); + assert("Mississippi".replace(["is", "i"], ["..", "*"]) == "M..s..s*pp*"); assert("Mississippi".replace("ss", "...") == "Mi...i...ippi"); assert("Hello".replace("ll", "y") == "Heyo"); - //assert("Hi".Replace(cast(string[])[], cast(string[])[]) == "Hi"); - assert("Speaker".replace([cast(string)"ea", "e"], ":") == "Sp:k:r"); - assert("Speaker".replace([cast(string)"e", "ea"], ":") == "Sp:ak:r"); + //assert("Hi".replace(cast(mstring[])[], cast(mstring[])[]) == "Hi"); + assert("Speaker".replace(["ea", "e"], ":") == "Sp:k:r"); + assert("Speaker".replace(["e", "ea"], ":") == "Sp:ak:r"); } /** @@ -382,30 +402,30 @@ * "Part1|Part2\r\n".escape("|\r\n", "|rn") == "Part1\\|Part2\\r\\n" * ----- */ -string escape(string str, char[] chars, char[] escChars) { +mstring escape(cstring str, const(char)[] chars, const(char)[] escChars) { if(chars.length != escChars.length) - throw new IllegalArgumentException("Escape(): chars and escChars must be same length"); + throw new IllegalArgumentException("escape(): chars and escChars must be same length"); sbuilder builder; loop: foreach(i, c; str) { foreach(j, c2; chars) { if(c == '\\') { // always escape backslash - builder.Add('\\'); - builder.Add('\\'); + builder.add('\\'); + builder.add('\\'); continue loop; } if(c == c2) { - builder.Add('\\'); - builder.Add(escChars[j]); + builder.add('\\'); + builder.add(escChars[j]); continue loop; } } - builder.Add(c); + builder.add(c); } - return builder.ToString(); + return builder.toString(); } /// ditto -string escape(string str) { +mstring escape(cstring str) { return str.escape("\t\r\n", "trn"); } unittest { @@ -425,35 +445,35 @@ * "test\\".unescape() * ----- */ -string unescape(string str, char[] escChars, char[] chars) { +mstring unescape(cstring str, const(char)[] escChars, const(char)[] chars) { if(escChars.length != chars.length) - throw new IllegalArgumentException("Unescape(): escChars and chars must be same length"); + throw new IllegalArgumentException("unescape(): escChars and chars must be same length"); sbuilder builder; loop: foreach(i, c; str) { if(c == '\\') { if(i == str.length-1) - throw new IllegalArgumentException("Unescape(): partial escape sequence at end of string"); + throw new IllegalArgumentException("unescape(): partial escape sequence at end of string"); if(str[i+1] == '\\') { - builder.Add('\\'); + builder.add('\\'); ++i; continue loop; } foreach(j, c2; escChars) { if(str[i+1] == c2) { - builder.Add(chars[j]); + builder.add(chars[j]); ++i; continue loop; } } - throw new IllegalArgumentException("Unescape(): invalid escape sequence"); + throw new IllegalArgumentException("unescape(): invalid escape sequence"); } - builder.Add(str[i]); + builder.add(str[i]); } - return builder.ToString(); + return builder.toString(); } /// ditto -string unescape(string str) { +mstring unescape(cstring str) { return str.unescape("trn", "\t\r\n"); } unittest { @@ -475,12 +495,12 @@ * "\t \n\r\f\v".removeWhitespace() == "" * ----- */ -string removeWhitespace(string str) { +mstring removeWhitespace(cstring str) { sbuilder builder; foreach(c; str) if(!" \t\n\r\v\f".contains(c)) - builder.Add(c); - return builder.ToString(); + builder.add(c); + return builder.toString(); } unittest { assert("4a d2 7c 3f".removeWhitespace() == "4ad27c3f"); @@ -500,12 +520,12 @@ * "".trim() == "" * ----- */ -string trim(string str) { +inout(char)[] trim(inout(char)[] str) { int start = -1, end = str.length; while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { } end++; if(end == 0) // means all whitespace - return ""; + return str[0..0]; while(" \t\n\r\v\f".contains(str[++start])) { } return str[start..end]; } @@ -528,7 +548,7 @@ * "".trimLeft() == "" * ----- */ -string trimLeft(string str) { +cstring trimLeft(cstring str) { int start = -1; while(++start < str.length && " \t\n\r\v\f".contains(str[start])) { } return str[start..$]; @@ -552,7 +572,7 @@ * "".trimRight() == "" * ----- */ -string trimRight(string str) { +cstring trimRight(cstring str) { int end = str.length; while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { } end++; @@ -576,9 +596,9 @@ assert(".NET Framework".find("") == 0); assert("Mississippi".findLast("ss") == 5); assert("Mississippi".findLast("ss", 4) == 2); - assert("Jordan=20".split("=") == [cast(string)"Jordan", "20"]); - assert("Jordan".split("") == [cast(string)"Jordan"]); - assert("Jordan".split1("=") == [cast(string)"Jordan"]); + assert("Jordan=20".split("=") == ["Jordan", "20"]); + assert("Jordan".split("") == ["Jordan"]); + assert("Jordan".split1("=") == ["Jordan"]); } /*class Encoding {