1 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
9 // (C) 2001 Ximian, Inc. http://www.ximian.com
12 // FIXME: from what I gather from msdn, when a function is to return an empty string
13 // we should be returning this.Empty - some methods do this and others don't.
15 // FIXME: I didn't realise until later that `string' has a .Length method and so
16 // I am missing some proper bounds-checking in some methods. Find these
17 // instances and throw the ArgumentOutOfBoundsException at the programmer.
18 // I like pelting programmers with ArgumentOutOfBoundsException's :-)
20 // FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
21 // need to be made unicode aware.
23 // FIXME: when you have a char carr[], does carr.Length include the terminating null char?
27 using System.Collections;
28 using System.Globalization;
29 using System.Runtime.CompilerServices;
33 //[DefaultMemberName("Chars")]
34 public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
35 public static readonly string Empty = "";
41 internal String (int storage)
44 throw new ArgumentOutOfRangeException ();
46 c_str = new char [storage];
50 unsafe public String (char *value)
54 // FIXME: can I do value.Length here?
58 for (i = 0; *(value + i) != '\0'; i++);
62 this.c_str = new char [this.length + 1];
63 for (i = 0; i < this.length; i++)
64 this.c_str[i] = *(value + i);
67 public String (char[] value)
71 // FIXME: value.Length includes the terminating null char?
72 this.length = value != null ? strlen (value): 0;
73 this.c_str = new char [this.length + 1];
74 for (i = 0; i < this.length; i++)
75 this.c_str[i] = value[i];
79 unsafe public String (sbyte *value)
81 // FIXME: consider unicode?
84 // FIXME: can I do value.Length here? */
88 for (i = 0; *(value + i) != '\0'; i++);
92 this.c_str = new char [this.length + 1];
93 for (i = 0; i < this.length; i++)
94 this.c_str[i] = (char) *(value + i);
97 public String (char c, int count)
102 this.c_str = new char [count + 1];
103 for (i = 0; i < count; i++)
107 [CLSCompliant(false)]
108 unsafe public String (char *value, int startIndex, int length)
112 if (value == null && startIndex != 0 && length != 0)
113 throw new ArgumentNullException ();
115 if (startIndex < 0 || length < 0)
116 throw new ArgumentOutOfRangeException ();
118 this.length = length;
119 this.c_str = new char [length + 1];
120 for (i = 0; i < length; i++)
121 this.c_str[i] = *(value + startIndex + i);
124 public String (char[] value, int startIndex, int length)
128 if (value == null && startIndex != 0 && length != 0)
129 throw new ArgumentNullException ();
131 if (startIndex < 0 || length < 0)
132 throw new ArgumentOutOfRangeException ();
134 this.length = length;
135 this.c_str = new char [length + 1];
136 for (i = 0; i < length; i++)
137 this.c_str[i] = value[startIndex + i];
140 [CLSCompliant(false)]
141 unsafe public String (sbyte *value, int startIndex, int length)
143 // FIXME: consider unicode?
146 if (value == null && startIndex != 0 && length != 0)
147 throw new ArgumentNullException ();
149 if (startIndex < 0 || length < 0)
150 throw new ArgumentOutOfRangeException ();
152 this.length = length;
153 this.c_str = new char [length + 1];
154 for (i = 0; i < length; i++)
155 this.c_str[i] = (char) *(value + startIndex + i);
158 [CLSCompliant(false)][MonoTODO]
159 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
161 // FIXME: implement me
166 // FIXME: is there anything we need to do here?
167 /*base.Finalize ();*/
177 [IndexerName("Chars")]
178 public char this [int index] {
180 if (index >= this.length)
181 throw new ArgumentOutOfRangeException ();
183 return this.c_str[index];
187 // Private helper methods
188 private static int strlen (char[] str)
190 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
195 private static char tolowerordinal (char c)
197 // FIXME: implement me
201 private static bool is_lwsp (char c)
203 /* this comes from the msdn docs for String.Trim() */
204 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
205 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
211 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
213 /* (hopefully) Unicode-safe Boyer-Moore implementation */
214 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
215 int h, n, he, ne, hc, nc, i;
217 if (haystack == null || needle == null)
218 throw new ArgumentNullException ();
220 /* if the search buffer is shorter than the pattern buffer, we can't match */
221 if (count < needle.length)
224 /* return an instant match if the pattern is 0-length */
225 if (needle.length == 0)
228 /* set a pointer at the end of each string */
229 ne = needle.length - 1; /* position of char before '\0' */
230 he = startIndex + count; /* position of last valid char */
232 /* init the skip table with the pattern length */
234 for (i = 0; i < 65536; i++)
237 /* set the skip value for the chars that *do* appear in the
238 * pattern buffer (needle) to the distance from the index to
239 * the end of the pattern buffer. */
240 for (nc = 0; nc < ne; nc++)
241 skiptable[(int) needle[nc]] = ne - nc;
244 while (count >= needle.length) {
245 hc = h + needle.length - 1; /* set the haystack compare pointer */
246 nc = ne; /* set the needle compare pointer */
248 /* work our way backwards until they don't match */
249 for (i = 0; nc > 0; nc--, hc--, i++)
250 if (needle[nc] != haystack[hc])
253 if (needle[nc] != haystack[hc]) {
254 n = skiptable[(int) haystack[hc]] - i;
266 public object Clone ()
268 // FIXME: implement me
272 public static int Compare (string strA, string strB)
281 } else if (strB == null)
284 min = strA.length < strB.length ? strA.length : strB.length;
286 for (i = 0; i < min; i++) {
287 if (strA.c_str[i] != strB.c_str[i]) {
288 return (int)strA.c_str[i] - (int)strB.c_str[i];
291 if (strA.length == strB.length) {
294 if (strA.length > min) {
301 public static int Compare (string strA, string strB, bool ignoreCase)
306 return Compare (strA, strB);
309 * And here I thought Eazel developers were on crack...
310 * if a string is null it should pelt the programmer with
311 * ArgumentNullExceptions, damnit!
318 } else if (strB == null)
321 min = strA.length < strB.length ? strA.length : strB.length;
323 for (i = 0; i < min; i++) {
324 if (Char.ToLower (strA.c_str[i]) != Char.ToLower (strB.c_str[i])) {
325 return (int)strA.c_str[i] - (int)strB.c_str[i];
328 if (strA.length == strB.length) {
331 if (strA.length > min) {
339 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
341 // FIXME: implement me
345 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
349 if (length < 0 || indexA < 0 || indexB < 0)
350 throw new ArgumentOutOfRangeException ();
352 if (indexA > strA.Length || indexB > strB.Length)
353 throw new ArgumentOutOfRangeException ();
355 /* And again with the ("" > null) logic... lord have mercy! */
361 } else if (strB == null)
364 for (i = 0; i < length - 1; i++) {
365 if (strA[indexA + i] != strB[indexB + i])
369 return ((int) (strA[indexA + i] - strB[indexB + i]));
372 public static int Compare (string strA, int indexA, string strB, int indexB,
373 int length, bool ignoreCase)
378 return Compare (strA, indexA, strB, indexB, length);
380 if (length < 0 || indexA < 0 || indexB < 0)
381 throw new ArgumentOutOfRangeException ();
383 if (indexA > strA.Length || indexB > strB.Length)
384 throw new ArgumentOutOfRangeException ();
386 /* When will the hurting stop!?!? */
392 } else if (strB == null)
395 for (i = 0; i < length - 1; i++) {
396 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
400 return ((int) (strA[indexA + i] - strB[indexB + i]));
404 public static int Compare (string strA, int indexA, string strB, int indexB,
405 int length, bool ignoreCase, CultureInfo culture)
408 throw new ArgumentNullException ();
410 if (length < 0 || indexA < 0 || indexB < 0)
411 throw new ArgumentOutOfRangeException ();
413 if (indexA > strA.Length || indexB > strB.Length)
414 throw new ArgumentOutOfRangeException ();
416 /* I can't take it anymore! */
422 } else if (strB == null)
425 // FIXME: implement me
429 public static int CompareOrdinal (string strA, string strB)
433 /* Please God, make it stop! */
439 } else if (strB == null)
442 for (i = 0; i < strA.Length && i < strB.Length; i++) {
445 cA = tolowerordinal (strA[i]);
446 cB = tolowerordinal (strB[i]);
452 return ((int) (strA[i] - strB[i]));
455 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
460 if (length < 0 || indexA < 0 || indexB < 0)
461 throw new ArgumentOutOfRangeException ();
468 } else if (strB == null)
471 for (i = 0; i < length; i++) {
474 cA = tolowerordinal (strA[indexA + i]);
475 cB = tolowerordinal (strB[indexB + i]);
481 return ((int) (strA[indexA + i] - strB[indexB + i]));
484 public int CompareTo (object obj)
486 return Compare (this, obj == null ? null : obj.ToString ());
489 public int CompareTo (string str)
491 return Compare (this, str);
494 public static string Concat (object arg)
496 return arg != null ? arg.ToString () : String.Empty;
499 public static string Concat (params object[] args)
506 throw new ArgumentNullException ();
508 strings = new string [args.Length];
511 foreach (object arg in args) {
512 /* use Empty for each null argument */
514 strings[i] = String.Empty;
516 strings[i] = arg.ToString ();
517 len += strings[i].length;
524 String res = new String (len);
527 for (int j = 0; j < strings.Length; j++)
528 for (int k = 0; k < strings[j].length; k++)
529 str[i++] = strings[j].c_str[k];
534 public static string Concat (params string[] values)
540 throw new ArgumentNullException ();
543 foreach (string value in values)
544 len += value != null ? value.Length : 0;
549 String res = new String (len);
552 foreach (string value in values) {
556 for (int j = 0; j < value.length; j++)
557 str[i++] = value.c_str[j];
563 public static string Concat (object arg0, object arg1)
565 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
566 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
568 return Concat (str0, str1);
571 public static string Concat (string str0, string str1)
581 len = str0.length + str1.length;
585 String res = new String (len);
588 for (i = 0; i < str0.length; i++)
589 concat[i] = str0.c_str[i];
590 for (j = 0 ; j < str1.length; j++)
591 concat[i + j] = str1.c_str[j];
596 public static string Concat (object arg0, object arg1, object arg2)
598 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
599 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
600 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
602 return Concat (str0, str1, str2);
605 public static string Concat (string str0, string str1, string str2)
617 len = str0.length + str1.length + str2.length;
621 String res = new String (len);
624 for (i = 0; i < str0.length; i++)
625 concat[i] = str0.c_str[i];
626 for (j = 0; j < str1.length; j++)
627 concat[i + j] = str1.c_str[j];
628 for (k = 0; k < str2.length; k++)
629 concat[i + j + k] = str2.c_str[k];
634 public static string Concat (string str0, string str1, string str2, string str3)
648 len = str0.length + str1.length + str2.length + str3.length;
651 String res = new String (len);
654 for (i = 0; i < str0.length; i++)
655 concat[i] = str0.c_str[i];
656 for (j = 0; j < str1.length; j++)
657 concat[i + j] = str1.c_str[j];
658 for (k = 0; k < str2.length; k++)
659 concat[i + j + k] = str2.c_str[k];
660 for (l = 0; l < str3.length; l++)
661 concat[i + j + k + l] = str3.c_str[l];
666 public static string Copy (string str)
668 // FIXME: how do I *copy* a string if I can only have 1 of each?
670 throw new ArgumentNullException ();
675 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
677 // LAMESPEC: should I null-terminate?
680 if (destination == null)
681 throw new ArgumentNullException ();
683 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
684 throw new ArgumentOutOfRangeException ();
686 if (sourceIndex + count > this.length)
687 throw new ArgumentOutOfRangeException ();
689 if (destinationIndex + count > destination.Length)
690 throw new ArgumentOutOfRangeException ();
692 for (i = 0; i < count; i++)
693 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
696 public bool EndsWith (string value)
698 bool endswith = true;
702 throw new ArgumentNullException ();
704 start = this.length - value.length;
708 for (i = start; i < this.length && endswith; i++)
709 endswith = this.c_str[i] == value.c_str[i - start];
714 public override bool Equals (object obj)
716 if (!(obj is String))
719 return this == (String) obj;
722 public bool Equals (string value)
724 return this == value;
727 public static bool Equals (string a, string b)
732 public static string Format (string format, object arg0) {
733 return Format (null, format, new object[] { arg0 });
736 public static string Format (string format, object arg0, object arg1) {
737 return Format (null, format, new object[] { arg0, arg1 });
740 public static string Format (string format, object arg0, object arg1, object arg2) {
741 return Format (null, format, new object[] { arg0, arg1, arg2 });
744 public static string Format (string format, params object[] args) {
745 return Format (null, format, args);
748 public static string Format (IFormatProvider provider, string format, params object[] args) {
749 if (format == null || args == null)
750 throw new ArgumentNullException ();
752 StringBuilder result = new StringBuilder ();
756 while (ptr < format.Length) {
757 char c = format[ptr ++];
760 result.Append (format, start, ptr - start - 1);
762 // check for escaped open bracket
764 if (format[ptr] == '{') {
775 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
776 if (n >= args.Length)
777 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
781 object arg = args[n];
786 else if (arg is IFormattable)
787 str = ((IFormattable)arg).ToString (arg_format, provider);
789 str = arg.ToString ();
791 // pad formatted string and append to result
793 if (width > str.Length) {
794 string pad = new String (' ', width - str.Length);
810 else if (c == '}' && format[ptr] == '}') {
811 result.Append (format, start, ptr - start - 1);
816 if (start < format.Length)
817 result.Append (format.Substring (start));
819 return result.ToString ();
822 public CharEnumerator GetEnumerator ()
824 return new CharEnumerator (this);
827 IEnumerator IEnumerable.GetEnumerator ()
829 return new CharEnumerator (this);
832 public override int GetHashCode ()
836 for (i = 0; i < length; ++i)
837 h = (h << 5) - h + c_str [i];
841 public TypeCode GetTypeCode ()
843 return TypeCode.String;
846 public int IndexOf (char value)
848 return IndexOf (value, 0, this.length);
851 public int IndexOf (string value)
853 return IndexOf (value, 0, this.length);
856 public int IndexOf (char value, int startIndex)
858 return IndexOf (value, startIndex, this.length - startIndex);
861 public int IndexOf (string value, int startIndex)
863 return IndexOf (value, startIndex, this.length - startIndex);
866 public int IndexOf (char value, int startIndex, int count)
870 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
871 throw new ArgumentOutOfRangeException ();
873 for (i = startIndex; i - startIndex < count; i++)
874 if (this.c_str[i] == value)
880 public int IndexOf (string value, int startIndex, int count)
883 throw new ArgumentNullException ();
885 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
886 throw new ArgumentOutOfRangeException ();
888 return BoyerMoore (this.c_str, value, startIndex, count);
891 for (i = startIndex; i - startIndex + value.Length <= count; ) {
892 if (this.c_str[i] == value[0]) {
896 for (j = 1; equal && j < value.Length; j++) {
897 equal = this.c_str[i + j] == value[j];
898 if (this.c_str[i + j] == value[0] && nexti == 0)
917 public int IndexOfAny (char[] values)
919 return IndexOfAny (values, 0, this.length);
922 public int IndexOfAny (char[] values, int startIndex)
924 return IndexOfAny (values, startIndex, this.length - startIndex);
927 public int IndexOfAny (char[] values, int startIndex, int count)
930 throw new ArgumentNullException ();
932 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
933 throw new ArgumentOutOfRangeException ();
935 for (int i = startIndex; i < startIndex + count; i++) {
936 for (int j = 0; j < strlen (values); j++) {
937 if (this.c_str[i] == values[j])
945 public string Insert (int startIndex, string value)
951 throw new ArgumentNullException ();
953 if (startIndex < 0 || startIndex > this.length)
954 throw new ArgumentOutOfRangeException ();
956 String res = new String (value.length + this.length);
959 for (i = 0; i < startIndex; i++)
960 str[i] = this.c_str[i];
961 for (j = 0; j < value.length; j++)
962 str[i + j] = value.c_str[j];
963 for ( ; i < this.length; i++)
964 str[i + j] = this.c_str[i];
969 [MethodImplAttribute(MethodImplOptions.InternalCall)]
970 public extern static string Intern (string str);
972 [MethodImplAttribute(MethodImplOptions.InternalCall)]
973 public extern static string IsInterned (string str);
975 public static string Join (string separator, string[] value)
977 return Join (separator, value, 0, value.Length);
980 public static string Join (string separator, string[] value, int startIndex, int count)
982 // LAMESPEC: msdn doesn't specify what happens when separator is null
986 if (separator == null || value == null)
987 throw new ArgumentNullException ();
989 if (startIndex + count > value.Length)
990 throw new ArgumentOutOfRangeException ();
993 for (i = startIndex, used = 0; used < count; i++, used++) {
995 len += separator.length;
997 len += value[i].length;
1000 // We have no elements to join?
1001 if (i == startIndex)
1002 return String.Empty;
1004 String res = new String (len);
1007 for (i = 0; i < value[startIndex].length; i++)
1008 str[i] = value[startIndex][i];
1011 for (j = startIndex + 1; used < count; j++, used++) {
1014 for (k = 0; k < separator.length; k++)
1015 str[i++] = separator.c_str[k];
1016 for (k = 0; k < value[j].length; k++)
1017 str[i++] = value[j].c_str[k];
1023 public int LastIndexOf (char value)
1025 int i = this.length;
1029 for (; i >= 0; i--) {
1030 if (this.c_str[i] == value)
1037 public int LastIndexOf (string value)
1039 return LastIndexOf (value, this.length - 1, this.length);
1042 public int LastIndexOf (char value, int startIndex)
1044 if (startIndex < 0 || startIndex >= this.length)
1045 throw new ArgumentOutOfRangeException ();
1047 for (int i = startIndex; i >= 0; i--) {
1048 if (this.c_str[i] == value)
1055 public int LastIndexOf (string value, int startIndex)
1057 return LastIndexOf (value, startIndex, startIndex + 1);
1060 public int LastIndexOf (char value, int startIndex, int count)
1062 if (startIndex < 0 || count < 0)
1063 throw new ArgumentOutOfRangeException ();
1065 if (startIndex >= this.length || startIndex - count + 1 < 0)
1066 throw new ArgumentOutOfRangeException ();
1068 for (int i = startIndex; i > startIndex - count; i--) {
1069 if (this.c_str[i] == value)
1076 public int LastIndexOf (string value, int startIndex, int count)
1078 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
1079 // but maybe it's the end-position in MS's implementation?
1080 // msdn is unclear on this point. I think this is correct though.
1084 throw new ArgumentNullException ();
1086 if (startIndex < 0 || startIndex >= this.length)
1087 throw new ArgumentOutOfRangeException ();
1089 if (count < 0 || startIndex - count + 1 < 0)
1090 throw new ArgumentOutOfRangeException ();
1092 if (value == String.Empty)
1095 if (startIndex + value.length > this.length) {
1096 /* just a little optimization */
1099 start = this.length - value.length;
1100 count -= startIndex - start;
1104 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1105 len = value.length - 1;
1106 for (i = startIndex; i > startIndex - count; i--) {
1107 if (this.c_str[i + len] == value.c_str[len]) {
1111 for (j = len - 1; equal && j >= 0; j--)
1112 equal = this.c_str[i + j] == value.c_str[j];
1122 public int LastIndexOfAny (char[] values)
1124 return LastIndexOfAny (values, this.length - 1, this.length);
1127 public int LastIndexOfAny (char[] values, int startIndex)
1129 return LastIndexOfAny (values, startIndex, startIndex + 1);
1132 public int LastIndexOfAny (char[] values, int startIndex, int count)
1137 throw new ArgumentNullException ();
1139 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1140 throw new ArgumentOutOfRangeException ();
1142 for (i = startIndex; i > startIndex - count; i--) {
1143 for (int j = 0; j < strlen (values); j++) {
1144 if (this.c_str[i] == values[j])
1152 public string PadLeft (int totalWidth)
1154 return PadLeft (totalWidth, ' ');
1157 public string PadLeft (int totalWidth, char padChar)
1163 throw new ArgumentException ();
1165 str = new char [totalWidth > this.length ? totalWidth : this.length];
1166 for (i = 0; i < totalWidth - this.length; i++)
1169 for (j = 0; j < this.length; i++, j++)
1170 str[i] = this.c_str[j];
1172 return new String (str);
1175 public string PadRight (int totalWidth)
1177 return PadRight (totalWidth, ' ');
1180 public string PadRight (int totalWidth, char padChar)
1186 throw new ArgumentException ();
1188 str = new char [totalWidth > this.length ? totalWidth : this.length];
1189 for (i = 0; i < this.length; i++)
1190 str[i] = this.c_str[i];
1192 for ( ; i < str.Length; i++)
1195 return new String (str);
1198 public string Remove (int startIndex, int count)
1203 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1204 throw new ArgumentOutOfRangeException ();
1206 len = this.length - count;
1208 return String.Empty;
1210 String res = new String (len);
1212 for (i = 0; i < startIndex; i++)
1213 str[i] = this.c_str[i];
1214 for (j = i + count; j < this.length; j++)
1215 str[i++] = this.c_str[j];
1220 public string Replace (char oldChar, char newChar)
1225 String res = new String (length);
1227 for (i = 0; i < this.length; i++) {
1228 if (this.c_str[i] == oldChar)
1231 str[i] = this.c_str[i];
1237 public string Replace (string oldValue, string newValue)
1239 // LAMESPEC: msdn doesn't specify what to do if either args is null
1240 int index, len, i, j;
1243 if (oldValue == null || newValue == null)
1244 throw new ArgumentNullException ();
1246 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1247 index = IndexOf (oldValue, 0);
1249 // This is the easy one ;-)
1250 return Substring (0, this.length);
1253 len = this.length - oldValue.length + newValue.length;
1255 return String.Empty;
1257 String res = new String (len);
1259 for (i = 0; i < index; i++)
1260 str[i] = this.c_str[i];
1261 for (j = 0; j < newValue.length; j++)
1262 str[i++] = newValue[j];
1263 for (j = index + oldValue.length; j < this.length; j++)
1264 str[i++] = this.c_str[j];
1269 private int splitme (char[] separators, int startIndex)
1271 /* this is basically a customized IndexOfAny() for the Split() methods */
1272 for (int i = startIndex; i < this.length; i++) {
1273 if (separators != null) {
1274 foreach (char sep in separators) {
1275 if (this.c_str[i] == sep)
1276 return i - startIndex;
1278 } else if (is_lwsp (this.c_str[i])) {
1279 return i - startIndex;
1286 public string[] Split (params char[] separator)
1290 * @separator: delimiting chars or null to split on whtspc
1292 * Returns: 1. An array consisting of a single
1293 * element (@this) if none of the delimiting
1294 * chars appear in @this. 2. An array of
1295 * substrings which are delimited by one of
1296 * the separator chars. 3. An array of
1297 * substrings separated by whitespace if
1298 * @separator is null. The Empty string should
1299 * be returned wherever 2 delimiting chars are
1302 // FIXME: would using a Queue be better?
1307 list = new ArrayList ();
1308 for (index = 0, len = 0; index < this.length; index += len + 1) {
1309 len = splitme (separator, index);
1310 len = len > -1 ? len : this.length - index;
1312 list.Add (String.Empty);
1317 str = new char [len];
1318 for (i = 0; i < len; i++)
1319 str[i] = this.c_str[index + i];
1321 list.Add (new String (str));
1325 strings = new string [list.Count];
1326 if (list.Count == 1) {
1327 /* special case for an array holding @this */
1330 for (index = 0; index < list.Count; index++)
1331 strings[index] = (string) list[index];
1337 public string[] Split (char[] separator, int maxCount)
1339 // FIXME: what to do if maxCount <= 0?
1340 // FIXME: would using Queue be better than ArrayList?
1343 int index, len, used;
1346 list = new ArrayList ();
1347 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1348 len = splitme (separator, index);
1349 len = len > -1 ? len : this.length - index;
1351 list.Add (String.Empty);
1356 str = new char [len];
1357 for (i = 0; i < len; i++)
1358 str[i] = this.c_str[index + i];
1360 list.Add (new String (str));
1365 /* fit the remaining chunk of the @this into it's own element */
1366 if (index < this.length - 1) {
1370 str = new char [this.length - index];
1371 for (i = index; i < this.length; i++)
1372 str[i - index] = this.c_str[i];
1374 list.Add (new String (str));
1377 strings = new string [list.Count];
1378 if (list.Count == 1) {
1379 /* special case for an array holding @this */
1382 for (index = 0; index < list.Count; index++)
1383 strings[index] = (string) list[index];
1389 public bool StartsWith (string value)
1391 bool startswith = true;
1395 throw new ArgumentNullException ();
1397 if (value.length > this.length)
1400 for (i = 0; i < value.length && startswith; i++)
1401 startswith = startswith && value.c_str[i] == this.c_str[i];
1406 public string Substring (int startIndex)
1411 if (startIndex < 0 || startIndex > this.length)
1412 throw new ArgumentOutOfRangeException ();
1414 len = this.length - startIndex;
1416 return String.Empty;
1417 String res = new String (len);
1419 for (i = startIndex; i < this.length; i++)
1420 str[i - startIndex] = this.c_str[i];
1425 public string Substring (int startIndex, int length)
1430 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1431 throw new ArgumentOutOfRangeException ();
1434 return String.Empty;
1436 String res = new String (length);
1438 for (i = startIndex; i < startIndex + length; i++)
1439 str[i - startIndex] = this.c_str[i];
1445 public bool ToBoolean (IFormatProvider provider)
1447 // FIXME: implement me
1448 throw new NotImplementedException ();
1452 public byte ToByte (IFormatProvider provider)
1454 // FIXME: implement me
1455 throw new NotImplementedException ();
1459 public char ToChar (IFormatProvider provider)
1461 // FIXME: implement me
1462 throw new NotImplementedException ();
1465 public char[] ToCharArray ()
1467 return ToCharArray (0, this.length);
1470 public char[] ToCharArray (int startIndex, int length)
1475 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1476 throw new ArgumentOutOfRangeException ();
1478 chars = new char [length];
1479 for (i = startIndex; i < length; i++)
1480 chars[i - startIndex] = this.c_str[i];
1486 public DateTime ToDateTime (IFormatProvider provider)
1488 // FIXME: implement me
1489 // return new DateTime (0);
1490 throw new NotImplementedException ();
1494 public decimal ToDecimal (IFormatProvider provider)
1496 // FIXME: implement me
1497 throw new NotImplementedException ();
1501 public double ToDouble (IFormatProvider provider)
1503 // FIXME: implement me
1504 throw new NotImplementedException ();
1508 public short ToInt16 (IFormatProvider provider)
1510 // FIXME: implement me
1511 throw new NotImplementedException ();
1515 public int ToInt32 (IFormatProvider provider)
1517 // FIXME: implement me
1518 throw new NotImplementedException ();
1522 public long ToInt64 (IFormatProvider provider)
1524 // FIXME: implement me
1525 throw new NotImplementedException ();
1528 public string ToLower ()
1533 String res = new String (length);
1535 for (i = 0; i < this.length; i++)
1536 str[i] = Char.ToLower (this.c_str[i]);
1542 public string ToLower (CultureInfo culture)
1544 // FIXME: implement me
1545 throw new NotImplementedException ();
1549 [CLSCompliant(false)][MonoTODO]
1550 public sbyte ToSByte (IFormatProvider provider)
1552 // FIXME: implement me
1553 throw new NotImplementedException ();
1557 public float ToSingle (IFormatProvider provider)
1559 // FIXME: implement me
1560 throw new NotImplementedException ();
1563 public override string ToString ()
1569 public string ToString (IFormatProvider format)
1571 // FIXME: implement me
1572 throw new NotImplementedException ();
1576 public object ToType (Type conversionType, IFormatProvider provider)
1578 // FIXME: implement me
1579 throw new NotImplementedException ();
1582 [CLSCompliant(false)][MonoTODO]
1583 public ushort ToUInt16 (IFormatProvider provider)
1585 // FIXME: implement me
1586 throw new NotImplementedException ();
1589 [CLSCompliant(false)][MonoTODO]
1590 public uint ToUInt32 (IFormatProvider provider)
1592 // FIXME: implement me
1593 throw new NotImplementedException ();
1596 [CLSCompliant(false)][MonoTODO]
1597 public ulong ToUInt64 (IFormatProvider provider)
1599 // FIXME: implement me
1600 throw new NotImplementedException ();
1603 public string ToUpper ()
1608 String res = new String (length);
1610 for (i = 0; i < this.length; i++)
1611 str[i] = Char.ToUpper (this.c_str[i]);
1617 public string ToUpper (CultureInfo culture)
1619 // FIXME: implement me
1620 throw new NotImplementedException ();
1623 public string Trim ()
1628 public string Trim (params char[] trimChars)
1631 bool matches = false;
1633 for (begin = 0; begin < this.length; begin++) {
1634 if (trimChars != null) {
1636 foreach (char c in trimChars) {
1637 matches = this.c_str[begin] == c;
1644 matches = is_lwsp (this.c_str[begin]);
1651 for (end = this.length - 1; end > begin; end--) {
1652 if (trimChars != null) {
1654 foreach (char c in trimChars) {
1655 matches = this.c_str[end] == c;
1662 matches = is_lwsp (this.c_str[end]);
1671 return String.Empty;
1673 return Substring (begin, end - begin);
1676 public string TrimEnd (params char[] trimChars)
1678 bool matches = true;
1681 for (end = this.length - 1; end > 0; end--) {
1682 if (trimChars != null) {
1684 foreach (char c in trimChars) {
1685 matches = this.c_str[end] == c;
1690 matches = is_lwsp (this.c_str[end]);
1695 return String.Empty;
1697 return Substring (0, end);
1700 public string TrimStart (params char[] trimChars)
1702 bool matches = true;
1705 for (begin = 0; matches && begin < this.length; begin++) {
1706 if (trimChars != null) {
1708 foreach (char c in trimChars) {
1709 matches = this.c_str[begin] == c;
1714 matches = is_lwsp (this.c_str[begin]);
1718 if (begin == this.length)
1719 return String.Empty;
1721 return Substring (begin, this.length - begin);
1725 public static bool operator ==(string a, string b)
1727 if ((object)a == null) {
1728 if ((object)b == null)
1732 if ((object)b == null)
1735 if (a.length != b.length)
1739 for (int i = 0; i < l; i++)
1740 if (a.c_str[i] != b.c_str[i])
1746 public static bool operator !=(string a, string b)
1753 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1754 // parses format specifier of form:
1760 // N = argument number (non-negative integer)
1762 n = ParseDecimal (str, ref ptr);
1764 throw new FormatException ("Input string was not in correct format.");
1766 // M = width (non-negative integer)
1768 if (str[ptr] == ',') {
1769 left_align = (str[++ ptr] == '-');
1773 width = ParseDecimal (str, ref ptr);
1775 throw new FormatException ("Input string was not in correct format.");
1782 // F = argument format (string)
1784 if (str[ptr] == ':') {
1786 while (str[ptr] != '}')
1789 format = str.Substring (start, ptr - start);
1794 if (str[ptr ++] != '}')
1795 throw new FormatException ("Input string was not in correct format.");
1797 catch (IndexOutOfRangeException) {
1798 throw new FormatException ("Input string was not in correct format.");
1802 private static int ParseDecimal (string str, ref int ptr) {
1807 if (c < '0' || '9' < c)
1810 n = n * 10 + c - '0';