0
|
1 // Written in the D programming language
|
|
2 // www.digitalmars.com/d/
|
|
3
|
|
4 /*
|
|
5 * The contents of this file are subject to the Mozilla Public License Version
|
|
6 * 1.1 (the "License"); you may not use this file except in compliance with
|
|
7 * the License. You may obtain a copy of the License at
|
|
8 * http://www.mozilla.org/MPL/
|
|
9 *
|
|
10 * Software distributed under the License is distributed on an "AS IS" basis,
|
|
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
12 * for the specific language governing rights and limitations under the
|
|
13 * License.
|
|
14 *
|
|
15 * The Original Code is the Dynamin library.
|
|
16 *
|
|
17 * The Initial Developer of the Original Code is Jordan Miner.
|
|
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009
|
|
19 * the Initial Developer. All Rights Reserved.
|
|
20 *
|
|
21 * Contributor(s):
|
|
22 * Jordan Miner <jminer7@gmail.com>
|
|
23 *
|
|
24 */
|
|
25
|
|
26 /**
|
|
27 * These functions should all return a new string if it is possible for them
|
|
28 * to ever modify their input. If they will never modify their input, they
|
|
29 * should always return a slice.
|
|
30 */
|
|
31 module dynamin.core.string;
|
|
32
|
|
33 import tango.core.Exception;
|
|
34 import tango.text.convert.Utf;
|
|
35 import tango.text.convert.Layout;
|
|
36 import tango.text.Unicode;
|
|
37 import tango.text.Util;
|
|
38 import dynamin.core.global;
|
|
39 import dynamin.core.math;
|
|
40
|
|
41 /// Defined as a char[]
|
|
42 alias char[] string;
|
|
43
|
|
44 ///
|
|
45 char* toCharPtr(char[] str) {
|
|
46 return (str~'\0').ptr;
|
|
47 }
|
|
48 ///
|
|
49 wchar* toWcharPtr(char[] str) {
|
|
50 return toString16(str~'\0').ptr;
|
|
51 }
|
|
52
|
|
53 /*
|
|
54 string ToString(ulong num, uint base = 10) {
|
|
55 if(base > 16)
|
|
56 throw new Exception("ToString() - radix more than 16");
|
|
57 char[] digits = "0123456789abcdef";
|
|
58 string str;
|
|
59 ulong div = base;
|
|
60 ulong prevDiv = 1;
|
|
61 do {
|
|
62 uint rem = num % div;
|
|
63 str ~= digits[rem/prevDiv];
|
|
64 prevDiv = div;
|
|
65 div *= base;
|
|
66 num -= rem;
|
|
67 } while(num > 0);
|
|
68 str.reverse;
|
|
69 return str;
|
|
70 }
|
|
71 */
|
|
72 Layout!(char) formatter;
|
|
73 static this() {
|
|
74 formatter = new Layout!(char);
|
|
75 }
|
|
76 string format(char[] str, ...) {
|
|
77 return formatter.convert(_arguments, _argptr, str);
|
|
78 }
|
|
79 unittest {
|
|
80 assert(format("I am {}", 20) == "I am 20");
|
|
81 }
|
|
82
|
|
83 /**
|
|
84 * Converts all lowercase characters in the specified string to uppercase.
|
|
85 * Examples:
|
|
86 * -----
|
|
87 * "Bounce the ball.".upcase() == "BOUNCE THE BALL."
|
|
88 * "Mañana".upcase() == "MAÑANA"
|
|
89 * "æóëø".upcase() == "ÆÓËØ"
|
|
90 * -----
|
|
91 */
|
|
92 string upcase(string str) {
|
|
93 return toUpper(str);
|
|
94 }
|
|
95 unittest {
|
|
96 assert("Bounce the ball.".upcase() == "BOUNCE THE BALL.");
|
|
97 assert("Mañana".upcase() == "MAÑANA");
|
|
98 assert("æóëø".upcase() == "ÆÓËØ");
|
|
99 }
|
|
100 /**
|
|
101 * Converts all uppercase characters in the specified string to lowercase.
|
|
102 * Examples:
|
|
103 * -----
|
|
104 * "BoUnCe ThE BALL.".downcase() == "bounce the ball."
|
|
105 * "MAÑANA".downcase() == "mañana"
|
|
106 * "ÆÓËØ".downcase() == "æóëø"
|
|
107 * -----
|
|
108 */
|
|
109 string downcase(string str) {
|
|
110 return toLower(str);
|
|
111 }
|
|
112 unittest {
|
|
113 assert("BoUnCe ThE BALL.".downcase() == "bounce the ball.");
|
|
114 assert("MAÑANA".downcase() == "mañana");
|
|
115 assert("ÆÓËØ".downcase() == "æóëø");
|
|
116 }
|
|
117
|
|
118 // TODO: make more use of delegates in these?
|
|
119 // TODO; use templates so that these work with wchar and dchar?
|
|
120 bool startsWith(string str, string subStr, int start = 0) {
|
|
121 if(start+subStr.length > str.length)
|
|
122 return false;
|
|
123 return str[start..start+subStr.length] == subStr;
|
|
124 }
|
|
125 bool endsWith(string str, string subStr) {
|
|
126 return endsWith(str, subStr, str.length);
|
|
127 }
|
|
128 bool endsWith(string str, string subStr, int start) {
|
|
129 if(start-subStr.length < 0)
|
|
130 return false;
|
|
131 return str[str.length-subStr.length..str.length] == subStr;
|
|
132 }
|
|
133 int findLast(string str, string subStr) {
|
|
134 return findLast(str, subStr, str.length);
|
|
135 }
|
|
136 int findLast(string str, string subStr, int start) {
|
|
137 for(int i = start-subStr.length; i >= 0; --i)
|
|
138 if(str[i..i+subStr.length] == subStr)
|
|
139 return i;
|
|
140 return -1;
|
|
141 }
|
|
142 int find(string str, string subStr, int start = 0) {
|
|
143 for(int i = start; i < str.length-subStr.length; ++i)
|
|
144 if(str[i..i+subStr.length] == subStr)
|
|
145 return i;
|
|
146 return -1;
|
|
147 }
|
|
148 /**
|
|
149 * Tests whether or not the specified char is in the specified string.
|
|
150 * Returns: true if the specified char is in the string and false otherwise
|
|
151 * Examples:
|
|
152 * -----
|
|
153 * "Hello".contains('e') == true
|
|
154 * "Hello".contains('a') == false
|
|
155 * "".contains('e') == false
|
|
156 * -----
|
|
157 */
|
|
158 bool contains(string str, char c) {
|
|
159 foreach(char c2; str) {
|
|
160 if(c == c2)
|
|
161 return true;
|
|
162 }
|
|
163 return false;
|
|
164 }
|
|
165 /// ditto
|
|
166 bool contains(string str, dchar c) {
|
|
167 foreach(dchar c2; str) {
|
|
168 if(c == c2)
|
|
169 return true;
|
|
170 }
|
|
171 return false;
|
|
172 }
|
|
173 unittest {
|
|
174 assert("Hello".contains('e') == true);
|
|
175 assert("Hello".contains('a') == false);
|
|
176 assert("".contains('e') == false);
|
|
177 }
|
|
178 string remove(string str, int start, int count = 1) {
|
|
179 return str[0..start] ~ str[start+count..str.length];
|
|
180 }
|
|
181 // TODO: ?
|
|
182 // split(string str, int delegate(string s) func)
|
|
183 //string[] split(string str, string subStr) {
|
|
184 // return split(str, (string s) { return s.startsWith(subStr) ? subStr.length, : -1; };
|
|
185 //}
|
|
186 // TODO: return slices to string
|
|
187 //split1("50=20=10", "=") -> ["50", "20=10"]
|
|
188 string[] split1(string str, string subStr) {
|
|
189 if(subStr.length == 0)
|
|
190 return [str];
|
|
191 int index = find(str, subStr);
|
|
192 if(index == -1)
|
|
193 return [str];
|
|
194 string[] strs = new string[2];
|
|
195 strs[0] = str[0..index].dup;
|
|
196 strs[1] = str[index+subStr.length..str.length].dup;
|
|
197 return strs;
|
|
198 }
|
|
199 // TODO: return slices to string
|
|
200 //split("50=20=10", "=") -> ["50", "20", "10"]
|
|
201 string[] split(string str, string subStr) {
|
|
202 if(subStr.length == 0)
|
|
203 return [str];
|
|
204 string[] strs;
|
|
205 int index, searchFrom;
|
|
206 int i = 0;
|
|
207 while(searchFrom < str.length) {
|
|
208 index = find(str, subStr, searchFrom);
|
|
209 if(index == -1) index = str.length;
|
|
210 strs.length = strs.length+1;
|
|
211 strs[i] = str[searchFrom..index].dup;
|
|
212 ++i;
|
|
213 searchFrom = index+subStr.length;
|
|
214 }
|
|
215 return strs;
|
|
216 }
|
|
217 ///
|
|
218 enum Newline {
|
|
219 ///
|
|
220 Cr = 0,
|
|
221 ///
|
|
222 Lf = 1,
|
|
223 ///
|
|
224 Crlf = 2,
|
|
225 ///
|
|
226 Macintosh = 0,
|
|
227 ///
|
|
228 Linux = 1,
|
|
229 ///
|
|
230 Windows = 2
|
|
231 }
|
|
232 /**
|
|
233 * Changes every occurrence of a newline in the specified string to the specified newline.
|
|
234 * Examples:
|
|
235 * -----
|
|
236 * "\r\n\n\r".convertNewlines(Newline.Lf) == "\n\n\n"
|
|
237 * "\r\n\n\r".convertNewlines(Newline.Windows) == "\r\n\r\n\r\n"
|
|
238 * "\n\r\n".convertNewlines(Newline.Macintosh) == "\r\r"
|
|
239 * -----
|
|
240 */
|
|
241 string convertNewlines(string str, Newline nl) {
|
|
242 string lineSep;
|
|
243 switch(nl) {
|
|
244 case Newline.Cr: lineSep = "\r"; break;
|
|
245 case Newline.Lf: lineSep = "\n"; break;
|
|
246 case Newline.Crlf: lineSep = "\r\n"; break;
|
|
247 }
|
|
248 return str.replace([cast(string)"\r\n", "\r", "\n"], lineSep);
|
|
249 }
|
|
250 unittest {
|
|
251 assert("\r\n\n\r".convertNewlines(Newline.Lf) == "\n\n\n");
|
|
252 assert("\r\n\n\r".convertNewlines(Newline.Windows) == "\r\n\r\n\r\n");
|
|
253 assert("\n\r\n".convertNewlines(Newline.Macintosh) == "\r\r");
|
|
254 }
|
|
255
|
|
256 /**
|
|
257 * Joins all the strings in the specified array together into one string, putting
|
|
258 * the specified separator between them.
|
|
259 * Examples:
|
|
260 * -----
|
|
261 * join(["10", "15", "17"], " - ") == "10 - 15 - 17"
|
|
262 * join(["789", "672", "484"], ",") == "789,672,484"
|
|
263 * join(["aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html"
|
|
264 * -----
|
|
265 */
|
|
266 string join(string[] strs, string sep) {
|
|
267 if(strs.length == 0)
|
|
268 return "";
|
|
269 int len;
|
|
270 foreach(string s; strs)
|
|
271 len += s.length;
|
|
272 len += sep.length*(strs.length-1);
|
|
273
|
|
274 string newStr = new char[len];
|
|
275 newStr[0..strs[0].length] = strs[0];
|
|
276 int start = strs[0].length;
|
|
277 for(int i = 1; i < strs.length; ++i) {
|
|
278 auto str = strs[i];
|
|
279 newStr[start..start+sep.length] = sep;
|
|
280 start += sep.length;
|
|
281 newStr[start..start+str.length] = str;
|
|
282 start += str.length;
|
|
283 }
|
|
284 return newStr;
|
|
285 }
|
|
286 unittest {
|
|
287 // TODO: remove cast(string) when D has bugs fixed
|
|
288 assert(join(["10", "15", "17"], " - ") == "10 - 15 - 17");
|
|
289 assert(join(["789", "672", "484"], ",") == "789,672,484");
|
|
290 assert(join([cast(string)"aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html");
|
|
291 }
|
|
292
|
|
293 /**
|
|
294 * Multiplies the given string the specified number of times.
|
|
295 * Returns: a string that is the result of adding the specified string onto
|
|
296 * an empty string the specified number of times
|
|
297 * Examples:
|
|
298 * -----
|
|
299 * "Hi...".times(3) == "Hi...Hi...Hi..."
|
|
300 * "0".times(20) == "00000000000000000000"
|
|
301 * "Hi".times(0) == ""
|
|
302 * -----
|
|
303 */
|
|
304 string times(string str, int n) {
|
|
305 string newStr = new char[n * str.length];
|
|
306 for(int i = 0; i < newStr.length; i += str.length)
|
|
307 newStr[i..i+str.length] = str;
|
|
308 return newStr;
|
|
309 }
|
|
310 unittest {
|
|
311 assert("0".times(4) == "0000");
|
|
312 assert("Hello! ".times(2) == "Hello! Hello! ");
|
|
313 assert("".times(50) == "");
|
|
314 assert("Hi".times(0) == "");
|
|
315 }
|
|
316
|
|
317 // TODO: flesh out and make public
|
|
318 struct sbuilder {
|
|
319 int Count;
|
|
320 string Data;
|
|
321 void Add(char c) {
|
|
322 if(Count + 1 > Data.length)
|
|
323 Data.length = (Data.length + 1) * 2;
|
|
324 Data[Count] = c;
|
|
325 ++Count;
|
|
326 }
|
|
327 void Add(string str) {
|
|
328 if(Count + str.length > Data.length)
|
|
329 Data.length = max((Data.length + 1) * 2, Count + str.length);
|
|
330 Data[Count..Count+str.length] = str;
|
|
331 Count += str.length;
|
|
332 }
|
|
333 string ToString() {
|
|
334 return Data[0..Count].dup;
|
|
335 }
|
|
336 }
|
|
337 /**
|
|
338 * Replaces any occurrence of a specified search string in the specified string
|
|
339 * with corresponding replacement string. The length of the searchStrs array
|
|
340 * must equal the length of the replacements array.
|
|
341 * Examples:
|
|
342 * -----
|
|
343 * "Mississippi".replace(["is", "i"], ["..", "*"]) == "M..s..s*pp*"
|
|
344 * "Mississippi".replace("ss", "...") == "Mi...i...ippi"
|
|
345 * "Hello".replace("ll", "y") == "Heyo"
|
|
346 * "Hi".replace([], []) == "Hi"
|
|
347 * -----
|
|
348 * Note: If multiple search strings have the same prefix, the longer search
|
|
349 * strings must be given first. Otherwise, any occurrence will match a
|
|
350 * shorter one and will not have a chance to match any longer one.
|
|
351 * Examples:
|
|
352 * -----
|
|
353 * "Speaker".replace(["ea", "e"], ":") == "Sp:k:r"
|
|
354 * "Speaker".replace(["e", "ea"], ":") == "Sp:ak:r"
|
|
355 * -----
|
|
356 * Bug: If a search string has a length of zero, this method will go into an infinite loop.
|
|
357 */
|
|
358 string replace(string str, string[] searchStrs, string[] replacements) {
|
|
359 if(replacements.length == 1 && searchStrs.length > 1) {
|
|
360 string tmp = replacements[0];
|
|
361 replacements = new string[searchStrs.length];
|
|
362 foreach(i, dummy; searchStrs)
|
|
363 replacements[i] = tmp;
|
|
364 }
|
|
365 if(searchStrs.length != replacements.length)
|
|
366 throw new IllegalArgumentException(
|
|
367 "Replace(): searchStrs and replacements must be same length");
|
|
368 sbuilder builder;
|
|
369 loop:
|
|
370 for(int i = 0; i < str.length; ) {
|
|
371 foreach(j, subStr; searchStrs) {
|
|
372 if(i+subStr.length <= str.length && str[i..i+subStr.length] == subStr) {
|
|
373 // skip the part of string that matched
|
|
374 i += subStr.length;
|
|
375 builder.Add(replacements[j]);
|
|
376 continue loop;
|
|
377 }
|
|
378 }
|
|
379 builder.Add(str[i]);
|
|
380 ++i;
|
|
381 }
|
|
382 return builder.ToString();
|
|
383 }
|
|
384 /// ditto
|
|
385 string replace(string str, string[] searchStrs, string replacement) {
|
|
386 return str.replace(searchStrs, [replacement]);
|
|
387 }
|
|
388 /// ditto
|
|
389 string replace(string str, string searchStr, string replacement) {
|
|
390 return str.replace([searchStr], [replacement]);
|
|
391 }
|
|
392 unittest {
|
|
393 assert("Mississippi".replace([cast(string)"is", "i"], [cast(string)"..", "*"]) == "M..s..s*pp*");
|
|
394 assert("Mississippi".replace("ss", "...") == "Mi...i...ippi");
|
|
395 assert("Hello".replace("ll", "y") == "Heyo");
|
|
396 //assert("Hi".Replace(cast(string[])[], cast(string[])[]) == "Hi");
|
|
397 assert("Speaker".replace([cast(string)"ea", "e"], ":") == "Sp:k:r");
|
|
398 assert("Speaker".replace([cast(string)"e", "ea"], ":") == "Sp:ak:r");
|
|
399 }
|
|
400
|
|
401 /**
|
|
402 * Changes every occurrence of a specified character in chars to the
|
|
403 * corresponding character in escChars.
|
|
404 * Examples:
|
|
405 * -----
|
|
406 * "Line1\r\nLine2\\".escape() == "Line1\\r\\nLine2\\\\"
|
|
407 * "Line1\tLine2".escape() == "Line1\\tLine2"
|
|
408 * "Part1|Part2\r\n".escape("|\r\n", "|rn") == "Part1\\|Part2\\r\\n"
|
|
409 * -----
|
|
410 */
|
|
411 string escape(string str, char[] chars, char[] escChars) {
|
|
412 if(chars.length != escChars.length)
|
|
413 throw new IllegalArgumentException("Escape(): chars and escChars must be same length");
|
|
414 sbuilder builder;
|
|
415 loop:
|
|
416 foreach(i, c; str) {
|
|
417 foreach(j, c2; chars) {
|
|
418 if(c == '\\') { // always escape backslash
|
|
419 builder.Add('\\');
|
|
420 builder.Add('\\');
|
|
421 continue loop;
|
|
422 }
|
|
423 if(c == c2) {
|
|
424 builder.Add('\\');
|
|
425 builder.Add(escChars[j]);
|
|
426 continue loop;
|
|
427 }
|
|
428 }
|
|
429 builder.Add(c);
|
|
430 }
|
|
431 return builder.ToString();
|
|
432 }
|
|
433 /// ditto
|
|
434 string escape(string str) {
|
|
435 return str.escape("\t\r\n", "trn");
|
|
436 }
|
|
437 unittest {
|
|
438 assert("Line1\r\nLine2\\".escape() == "Line1\\r\\nLine2\\\\");
|
|
439 assert("Line1\tLine2".escape() == "Line1\\tLine2");
|
|
440 assert("Part1|Part2\r\n".escape("|\r\n", "|rn") == "Part1\\|Part2\\r\\n");
|
|
441 }
|
|
442 /**
|
|
443 * Changes every occurrence of a specified character in escChars to the
|
|
444 * corresponding character in chars.
|
|
445 * Examples:
|
|
446 * -----
|
|
447 * "Line1\\r\\nLine2".unescape() == "Line1\r\nLine2"
|
|
448 * "Line1\\tLine2".unescape() == "Line1\tLine2"
|
|
449 * "Part1\\|Part2\\r\\n".unescape("|rn", "|\r\n") == "Part1|Part2\r\n"
|
|
450 * // error:
|
|
451 * "test\\".unescape()
|
|
452 * -----
|
|
453 */
|
|
454 string unescape(string str, char[] escChars, char[] chars) {
|
|
455 if(escChars.length != chars.length)
|
|
456 throw new IllegalArgumentException("Unescape(): escChars and chars must be same length");
|
|
457 sbuilder builder;
|
|
458 loop:
|
|
459 foreach(i, c; str) {
|
|
460 if(c == '\\') {
|
|
461 if(i == str.length-1)
|
|
462 throw new IllegalArgumentException("Unescape(): partial escape sequence at end of string");
|
|
463 if(str[i+1] == '\\') {
|
|
464 builder.Add('\\');
|
|
465 ++i;
|
|
466 continue loop;
|
|
467 }
|
|
468 foreach(j, c2; escChars) {
|
|
469 if(str[i+1] == c2) {
|
|
470 builder.Add(chars[j]);
|
|
471 ++i;
|
|
472 continue loop;
|
|
473 }
|
|
474 }
|
|
475 throw new IllegalArgumentException("Unescape(): invalid escape sequence");
|
|
476 }
|
|
477 builder.Add(str[i]);
|
|
478 }
|
|
479 return builder.ToString();
|
|
480 }
|
|
481 /// ditto
|
|
482 string unescape(string str) {
|
|
483 return str.unescape("trn", "\t\r\n");
|
|
484 }
|
|
485 unittest {
|
|
486 assert("Line1\\r\\nLine2\\\\".unescape() == "Line1\r\nLine2\\");
|
|
487 assert("Line1\\tLine2".unescape() == "Line1\tLine2");
|
|
488 assert("Part1\\|Part2\\r\\n".unescape("|rn", "|\r\n") == "Part1|Part2\r\n");
|
|
489 }
|
|
490 unittest {
|
|
491 string str = r"C:\\n";
|
|
492 assert(str.escape().unescape() == str);
|
|
493 }
|
|
494 /**
|
|
495 * Removes all whitespace characters from the specified string.
|
|
496 * Examples:
|
|
497 * -----
|
|
498 * "4a d2 7c 3f".removeWhitespace() == "4ad27c3f"
|
|
499 * " Hello \r\n".removeWhitespace() == "Hello"
|
|
500 * "How are you?".removeWhitespace() == "Howareyou?"
|
|
501 * "\t \n\r\f\v".removeWhitespace() == ""
|
|
502 * -----
|
|
503 */
|
|
504 string removeWhitespace(string str) {
|
|
505 sbuilder builder;
|
|
506 foreach(c; str)
|
|
507 if(!" \t\n\r\v\f".contains(c))
|
|
508 builder.Add(c);
|
|
509 return builder.ToString();
|
|
510 }
|
|
511 unittest {
|
|
512 assert("4a d2 7c 3f".removeWhitespace() == "4ad27c3f");
|
|
513 assert(" Hello \r\n".removeWhitespace() == "Hello");
|
|
514 assert("How are you?".removeWhitespace() == "Howareyou?");
|
|
515 assert("\t \n\r\f\v".removeWhitespace() == "");
|
|
516 }
|
|
517 /**
|
|
518 * Removes all the whitespace characters from the start and from the end
|
|
519 * of the specified string. Returns a slice.
|
|
520 * Examples:
|
|
521 * -----
|
|
522 * " Hello \r\n".trim() == "Hello"
|
|
523 * "How are you?".trim() == "How are you?"
|
|
524 * "\n la di da ".trim() == "la di da"
|
|
525 * " \n".trim() == ""
|
|
526 * "".trim() == ""
|
|
527 * -----
|
|
528 */
|
|
529 string trim(string str) {
|
|
530 int start = -1, end = str.length;
|
|
531 while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { }
|
|
532 end++;
|
|
533 if(end == 0) // means all whitespace
|
|
534 return "";
|
|
535 while(" \t\n\r\v\f".contains(str[++start])) { }
|
|
536 return str[start..end];
|
|
537 }
|
|
538 unittest {
|
|
539 assert(" Hello \r\n".trim() == "Hello");
|
|
540 assert("How are you?".trim() == "How are you?");
|
|
541 assert("\n la di da ".trim() == "la di da");
|
|
542 assert(" \n".trim() == "");
|
|
543 assert("".trim() == "");
|
|
544 }
|
|
545 /**
|
|
546 * Removes all the whitespace characters from the start
|
|
547 * of the specified string. Returns a slice.
|
|
548 * Examples:
|
|
549 * -----
|
|
550 * " Hello \r\n".trimLeft() == "Hello \r\n"
|
|
551 * "How are you?".trimLeft() == "How are you?"
|
|
552 * "\n la di da ".trimLeft() == "la di da "
|
|
553 * " \n".trimLeft() == ""
|
|
554 * "".trimLeft() == ""
|
|
555 * -----
|
|
556 */
|
|
557 string trimLeft(string str) {
|
|
558 int start = -1;
|
|
559 while(++start < str.length && " \t\n\r\v\f".contains(str[start])) { }
|
|
560 return str[start..$];
|
|
561 }
|
|
562 unittest {
|
|
563 assert(" Hello \r\n".trimLeft() == "Hello \r\n");
|
|
564 assert("How are you?".trimLeft() == "How are you?");
|
|
565 assert("\n la di da ".trimLeft() == "la di da ");
|
|
566 assert(" \n".trimLeft() == "");
|
|
567 assert("".trimLeft() == "");
|
|
568 }
|
|
569 /**
|
|
570 * Removes all the whitespace characters from the start
|
|
571 * of the specified string. Returns a slice.
|
|
572 * Examples:
|
|
573 * -----
|
|
574 * " Hello \r\n".trimRight() == " Hello"
|
|
575 * "How are you?".trimRight() == "How are you?"
|
|
576 * "\n la di da ".trimRight() == "\n la di da"
|
|
577 * " \n".trimRight() == ""
|
|
578 * "".trimRight() == ""
|
|
579 * -----
|
|
580 */
|
|
581 string trimRight(string str) {
|
|
582 int end = str.length;
|
|
583 while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { }
|
|
584 end++;
|
|
585 return str[0..end];
|
|
586 }
|
|
587 unittest {
|
|
588 assert(" Hello \r\n".trimRight() == " Hello");
|
|
589 assert("How are you?".trimRight() == "How are you?");
|
|
590 assert("\n la di da ".trimRight() == "\n la di da");
|
|
591 assert(" \n".trimRight() == "");
|
|
592 assert("".trimRight() == "");
|
|
593 }
|
|
594
|
|
595
|
|
596 unittest {
|
|
597 assert(".NET Framework".startsWith(".N"));
|
|
598 assert(".NET Framework".startsWith("Frame", 5));
|
|
599 assert(!".NET Framework".startsWith(".NEW"));
|
|
600 assert(".NET Framework".find("NET") == 1);
|
|
601 assert(".NET Framework".find("NET", 2) == -1);
|
|
602 assert(".NET Framework".find("") == 0);
|
|
603 assert("Mississippi".findLast("ss") == 5);
|
|
604 assert("Mississippi".findLast("ss", 4) == 2);
|
|
605 assert("Jordan=20".split("=") == [cast(string)"Jordan", "20"]);
|
|
606 assert("Jordan".split("") == [cast(string)"Jordan"]);
|
|
607 assert("Jordan".split1("=") == [cast(string)"Jordan"]);
|
|
608 }
|
|
609
|
|
610 /*class Encoding {
|
|
611 private:
|
|
612 //TODO: remove dependency on std.utf
|
|
613 static Encoding[] encodings = [
|
|
614 //new Encoding("windows-1252".Str(), "Western European (Windows)".Str(), encodeTableWindows1252)
|
|
615 ];
|
|
616 String name;
|
|
617 String desc;
|
|
618 public:
|
|
619 //property
|
|
620 static Encoding[] Encodings() {
|
|
621 return encodings;
|
|
622 }
|
|
623 static Encoding GetEncoding(String name) {
|
|
624 }
|
|
625 static byte[] Convert(Encoding src, Encoding dst, byte[] bytes) {}
|
|
626
|
|
627 this(String name, String description, wchar[] table) {
|
|
628 this.name = name;
|
|
629 }
|
|
630 // example: utf-8
|
|
631 String Name() {
|
|
632 return name;
|
|
633 }
|
|
634 // example: Unicode (UTF-8)
|
|
635 String Description() {
|
|
636 return desc;
|
|
637 }
|
|
638 int GetEncodedCount() {}
|
|
639 byte[] Encode(String str) {
|
|
640 }
|
|
641 //Returns the number of characters
|
|
642 int GetDecodedLength() {
|
|
643 }
|
|
644 String Decode(byte[] bytes) {
|
|
645 }
|
|
646 }*/
|
|
647 //class Utf8Encoding : Encoding {
|
|
648 //public:
|
|
649 //}
|
|
650
|
|
651 //characters that cannot be mapped to unicode are 0xFFFD in the tables
|
|
652 wchar[] encodeTableWindows1252 = [
|
|
653 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
|
|
654 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
|
|
655 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
|
|
656 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
|
|
657 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
|
|
658 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
|
|
659 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
|
|
660 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
661 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
|
662 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
|
|
663 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
|
|
664 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
|
|
665 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
|
|
666 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
|
|
667 0x007E, 0x007F, 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020,
|
|
668 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFD, 0x017D, 0xFFFD,
|
|
669 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC,
|
|
670 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFD, 0x017E, 0x0178, 0x00A0, 0x00A1,
|
|
671 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA,
|
|
672 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3,
|
|
673 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC,
|
|
674 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5,
|
|
675 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE,
|
|
676 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
|
677 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0,
|
|
678 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9,
|
|
679 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2,
|
|
680 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB,
|
|
681 0x00FC, 0x00FD, 0x00FE, 0x00FF
|
|
682 ];
|
|
683
|
|
684 //iso8859-1 does not need a conversion table, as its values are all the same as Unicode's
|
|
685
|
|
686 wchar[] encodeTableIso8859_2 = [
|
|
687 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
|
|
688 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
|
|
689 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
|
|
690 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
|
|
691 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
|
|
692 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
|
|
693 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
|
|
694 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
695 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
|
696 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
|
|
697 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
|
|
698 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
|
|
699 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
|
|
700 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
|
|
701 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
|
|
702 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
|
|
703 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098,
|
|
704 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x0104,
|
|
705 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E,
|
|
706 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142,
|
|
707 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A,
|
|
708 0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139,
|
|
709 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE,
|
|
710 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
|
|
711 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155,
|
|
712 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9,
|
|
713 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148,
|
|
714 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171,
|
|
715 0x00FC, 0x00FD, 0x0163, 0x02D9
|
|
716 ];
|
|
717
|
|
718 wchar[] encodeTableIso8859_3 = [
|
|
719 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
|
|
720 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
|
|
721 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
|
|
722 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
|
|
723 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
|
|
724 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
|
|
725 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
|
|
726 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
727 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
|
728 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
|
|
729 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
|
|
730 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
|
|
731 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
|
|
732 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
|
|
733 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
|
|
734 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
|
|
735 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098,
|
|
736 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x0126,
|
|
737 0x02D8, 0x00A3, 0x00A4, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E,
|
|
738 0x0134, 0x00AD, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
|
|
739 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x017C,
|
|
740 0x00C0, 0x00C1, 0x00C2, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9,
|
|
741 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3,
|
|
742 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC,
|
|
743 0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E4, 0x010B, 0x0109,
|
|
744 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
|
745 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9,
|
|
746 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
|
|
747 ];
|