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 c_str = new char [storage];
48 unsafe public String (char *value)
52 // FIXME: can I do value.Length here?
56 for (i = 0; *(value + i) != '\0'; i++);
60 this.c_str = new char [this.length + 1];
61 for (i = 0; i < this.length; i++)
62 this.c_str[i] = *(value + i);
65 public String (char[] value)
69 // FIXME: value.Length includes the terminating null char?
70 this.length = value != null ? strlen (value): 0;
71 this.c_str = new char [this.length + 1];
72 for (i = 0; i < this.length; i++)
73 this.c_str[i] = value[i];
77 unsafe public String (sbyte *value)
79 // FIXME: consider unicode?
82 // FIXME: can I do value.Length here? */
86 for (i = 0; *(value + i) != '\0'; i++);
90 this.c_str = new char [this.length + 1];
91 for (i = 0; i < this.length; i++)
92 this.c_str[i] = (char) *(value + i);
95 public String (char c, int count)
100 this.c_str = new char [count + 1];
101 for (i = 0; i < count; i++)
105 [CLSCompliant(false)]
106 unsafe public String (char *value, int startIndex, int length)
110 if (value == null && startIndex != 0 && length != 0)
111 throw new ArgumentNullException ();
113 if (startIndex < 0 || length < 0)
114 throw new ArgumentOutOfRangeException ();
116 this.length = length;
117 this.c_str = new char [length + 1];
118 for (i = 0; i < length; i++)
119 this.c_str[i] = *(value + startIndex + i);
122 public String (char[] value, int startIndex, int length)
126 if (value == null && startIndex != 0 && length != 0)
127 throw new ArgumentNullException ();
129 if (startIndex < 0 || length < 0)
130 throw new ArgumentOutOfRangeException ();
132 this.length = length;
133 this.c_str = new char [length + 1];
134 for (i = 0; i < length; i++)
135 this.c_str[i] = value[startIndex + i];
138 [CLSCompliant(false)]
139 unsafe public String (sbyte *value, int startIndex, int length)
141 // FIXME: consider unicode?
144 if (value == null && startIndex != 0 && length != 0)
145 throw new ArgumentNullException ();
147 if (startIndex < 0 || length < 0)
148 throw new ArgumentOutOfRangeException ();
150 this.length = length;
151 this.c_str = new char [length + 1];
152 for (i = 0; i < length; i++)
153 this.c_str[i] = (char) *(value + startIndex + i);
156 [CLSCompliant(false)][MonoTODO]
157 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
159 // FIXME: implement me
164 // FIXME: is there anything we need to do here?
165 /*base.Finalize ();*/
175 [IndexerName("Chars")]
176 public char this [int index] {
178 if (index >= this.length)
179 throw new ArgumentOutOfRangeException ();
181 return this.c_str[index];
185 // Private helper methods
186 private static int strlen (char[] str)
188 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
193 private static char tolowerordinal (char c)
195 // FIXME: implement me
199 private static bool is_lwsp (char c)
201 /* this comes from the msdn docs for String.Trim() */
202 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
203 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
209 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
211 /* (hopefully) Unicode-safe Boyer-Moore implementation */
212 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
213 int h, n, he, ne, hc, nc, i;
215 if (haystack == null || needle == null)
216 throw new ArgumentNullException ();
218 /* if the search buffer is shorter than the pattern buffer, we can't match */
219 if (count < needle.length)
222 /* return an instant match if the pattern is 0-length */
223 if (needle.length == 0)
226 /* set a pointer at the end of each string */
227 ne = needle.length - 1; /* position of char before '\0' */
228 he = startIndex + count; /* position of last valid char */
230 /* init the skip table with the pattern length */
232 for (i = 0; i < 65536; i++)
235 /* set the skip value for the chars that *do* appear in the
236 * pattern buffer (needle) to the distance from the index to
237 * the end of the pattern buffer. */
238 for (nc = 0; nc < ne; nc++)
239 skiptable[(int) needle[nc]] = ne - nc;
242 while (count >= needle.length) {
243 hc = h + needle.length - 1; /* set the haystack compare pointer */
244 nc = ne; /* set the needle compare pointer */
246 /* work our way backwards until they don't match */
247 for (i = 0; nc > 0; nc--, hc--, i++)
248 if (needle[nc] != haystack[hc])
251 if (needle[nc] != haystack[hc]) {
252 n = skiptable[(int) haystack[hc]] - i;
264 public object Clone ()
266 // FIXME: implement me
270 public static int Compare (string strA, string strB)
279 } else if (strB == null)
282 min = strA.length < strB.length ? strA.length : strB.length;
284 for (i = 0; i < min; i++) {
285 if (strA.c_str[i] != strB.c_str[i]) {
286 return (int)strA.c_str[i] - (int)strB.c_str[i];
289 if (strA.length == strB.length) {
292 if (strA.length > min) {
299 public static int Compare (string strA, string strB, bool ignoreCase)
304 return Compare (strA, strB);
307 * And here I thought Eazel developers were on crack...
308 * if a string is null it should pelt the programmer with
309 * ArgumentNullExceptions, damnit!
316 } else if (strB == null)
319 min = strA.length < strB.length ? strA.length : strB.length;
321 for (i = 0; i < min; i++) {
322 if (Char.ToLower (strA.c_str[i]) != Char.ToLower (strB.c_str[i])) {
323 return (int)strA.c_str[i] - (int)strB.c_str[i];
326 if (strA.length == strB.length) {
329 if (strA.length > min) {
337 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
339 // FIXME: implement me
343 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
347 if (length < 0 || indexA < 0 || indexB < 0)
348 throw new ArgumentOutOfRangeException ();
350 if (indexA > strA.Length || indexB > strB.Length)
351 throw new ArgumentOutOfRangeException ();
353 /* And again with the ("" > null) logic... lord have mercy! */
359 } else if (strB == null)
362 for (i = 0; i < length - 1; i++) {
363 if (strA[indexA + i] != strB[indexB + i])
367 return ((int) (strA[indexA + i] - strB[indexB + i]));
370 public static int Compare (string strA, int indexA, string strB, int indexB,
371 int length, bool ignoreCase)
376 return Compare (strA, indexA, strB, indexB, length);
378 if (length < 0 || indexA < 0 || indexB < 0)
379 throw new ArgumentOutOfRangeException ();
381 if (indexA > strA.Length || indexB > strB.Length)
382 throw new ArgumentOutOfRangeException ();
384 /* When will the hurting stop!?!? */
390 } else if (strB == null)
393 for (i = 0; i < length - 1; i++) {
394 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
398 return ((int) (strA[indexA + i] - strB[indexB + i]));
402 public static int Compare (string strA, int indexA, string strB, int indexB,
403 int length, bool ignoreCase, CultureInfo culture)
406 throw new ArgumentNullException ();
408 if (length < 0 || indexA < 0 || indexB < 0)
409 throw new ArgumentOutOfRangeException ();
411 if (indexA > strA.Length || indexB > strB.Length)
412 throw new ArgumentOutOfRangeException ();
414 /* I can't take it anymore! */
420 } else if (strB == null)
423 // FIXME: implement me
427 public static int CompareOrdinal (string strA, string strB)
431 /* Please God, make it stop! */
437 } else if (strB == null)
440 for (i = 0; i < strA.Length && i < strB.Length; i++) {
443 cA = tolowerordinal (strA[i]);
444 cB = tolowerordinal (strB[i]);
450 return ((int) (strA[i] - strB[i]));
453 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
458 if (length < 0 || indexA < 0 || indexB < 0)
459 throw new ArgumentOutOfRangeException ();
466 } else if (strB == null)
469 for (i = 0; i < length; i++) {
472 cA = tolowerordinal (strA[indexA + i]);
473 cB = tolowerordinal (strB[indexB + i]);
479 return ((int) (strA[indexA + i] - strB[indexB + i]));
482 public int CompareTo (object obj)
484 return Compare (this, obj == null ? null : obj.ToString ());
487 public int CompareTo (string str)
489 return Compare (this, str);
492 public static string Concat (object arg)
494 return arg != null ? arg.ToString () : String.Empty;
497 public static string Concat (params object[] args)
504 throw new ArgumentNullException ();
506 strings = new string [args.Length];
509 foreach (object arg in args) {
510 /* use Empty for each null argument */
512 strings[i] = String.Empty;
514 strings[i] = arg.ToString ();
515 len += strings[i].length;
522 String res = new String (len);
525 for (int j = 0; j < strings.Length; j++)
526 for (int k = 0; k < strings[j].length; k++)
527 str[i++] = strings[j].c_str[k];
532 public static string Concat (params string[] values)
538 throw new ArgumentNullException ();
541 foreach (string value in values)
542 len += value != null ? value.Length : 0;
547 String res = new String (len);
550 foreach (string value in values) {
554 for (int j = 0; j < value.length; j++)
555 str[i++] = value.c_str[j];
561 public static string Concat (object arg0, object arg1)
563 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
564 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
566 return Concat (str0, str1);
569 public static string Concat (string str0, string str1)
579 len = str0.length + str1.length;
583 String res = new String (len);
586 for (i = 0; i < str0.length; i++)
587 concat[i] = str0.c_str[i];
588 for (j = 0 ; j < str1.length; j++)
589 concat[i + j] = str1.c_str[j];
594 public static string Concat (object arg0, object arg1, object arg2)
596 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
597 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
598 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
600 return Concat (str0, str1, str2);
603 public static string Concat (string str0, string str1, string str2)
615 len = str0.length + str1.length + str2.length;
619 String res = new String (len);
622 for (i = 0; i < str0.length; i++)
623 concat[i] = str0.c_str[i];
624 for (j = 0; j < str1.length; j++)
625 concat[i + j] = str1.c_str[j];
626 for (k = 0; k < str2.length; k++)
627 concat[i + j + k] = str2.c_str[k];
632 public static string Concat (string str0, string str1, string str2, string str3)
646 len = str0.length + str1.length + str2.length + str3.length;
649 String res = new String (len);
652 for (i = 0; i < str0.length; i++)
653 concat[i] = str0.c_str[i];
654 for (j = 0; j < str1.length; j++)
655 concat[i + j] = str1.c_str[j];
656 for (k = 0; k < str2.length; k++)
657 concat[i + j + k] = str2.c_str[k];
658 for (l = 0; l < str3.length; l++)
659 concat[i + j + k + l] = str3.c_str[l];
664 public static string Copy (string str)
666 // FIXME: how do I *copy* a string if I can only have 1 of each?
668 throw new ArgumentNullException ();
673 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
675 // LAMESPEC: should I null-terminate?
678 if (destination == null)
679 throw new ArgumentNullException ();
681 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
682 throw new ArgumentOutOfRangeException ();
684 if (sourceIndex + count > this.length)
685 throw new ArgumentOutOfRangeException ();
687 if (destinationIndex + count > destination.Length)
688 throw new ArgumentOutOfRangeException ();
690 for (i = 0; i < count; i++)
691 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
694 public bool EndsWith (string value)
696 bool endswith = true;
700 throw new ArgumentNullException ();
702 start = this.length - value.length;
706 for (i = start; i < this.length && endswith; i++)
707 endswith = this.c_str[i] == value.c_str[i - start];
712 public override bool Equals (object obj)
714 if (!(obj is String))
717 return this == (String) obj;
720 public bool Equals (string value)
722 return this == value;
725 public static bool Equals (string a, string b)
730 public static string Format (string format, object arg0) {
731 return Format (null, format, new object[] { arg0 });
734 public static string Format (string format, object arg0, object arg1) {
735 return Format (null, format, new object[] { arg0, arg1 });
738 public static string Format (string format, object arg0, object arg1, object arg2) {
739 return Format (null, format, new object[] { arg0, arg1, arg2 });
742 public static string Format (string format, params object[] args) {
743 return Format (null, format, args);
746 public static string Format (IFormatProvider provider, string format, params object[] args) {
747 if (format == null || args == null)
748 throw new ArgumentNullException ();
750 StringBuilder result = new StringBuilder ();
754 while (ptr < format.Length) {
755 char c = format[ptr ++];
758 result.Append (format, start, ptr - start - 1);
760 // check for escaped open bracket
762 if (format[ptr] == '{') {
773 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
774 if (n >= args.Length)
775 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
779 object arg = args[n];
784 else if (arg is IFormattable)
785 str = ((IFormattable)arg).ToString (arg_format, provider);
787 str = arg.ToString ();
789 // pad formatted string and append to result
791 if (width > str.Length) {
792 string pad = new String (' ', width - str.Length);
808 else if (c == '}' && format[ptr] == '}') {
809 result.Append (format, start, ptr - start - 1);
814 if (start < format.Length)
815 result.Append (format.Substring (start));
817 return result.ToString ();
820 public CharEnumerator GetEnumerator ()
822 return new CharEnumerator (this);
825 IEnumerator IEnumerable.GetEnumerator ()
827 return new CharEnumerator (this);
830 public override int GetHashCode ()
834 for (i = 0; i < length; ++i)
835 h = (h << 5) - h + c_str [i];
839 public TypeCode GetTypeCode ()
841 return TypeCode.String;
844 public int IndexOf (char value)
846 return IndexOf (value, 0, this.length);
849 public int IndexOf (string value)
851 return IndexOf (value, 0, this.length);
854 public int IndexOf (char value, int startIndex)
856 return IndexOf (value, startIndex, this.length - startIndex);
859 public int IndexOf (string value, int startIndex)
861 return IndexOf (value, startIndex, this.length - startIndex);
864 public int IndexOf (char value, int startIndex, int count)
868 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
869 throw new ArgumentOutOfRangeException ();
871 for (i = startIndex; i - startIndex < count; i++)
872 if (this.c_str[i] == value)
878 public int IndexOf (string value, int startIndex, int count)
881 throw new ArgumentNullException ();
883 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
884 throw new ArgumentOutOfRangeException ();
886 return BoyerMoore (this.c_str, value, startIndex, count);
889 for (i = startIndex; i - startIndex + value.Length <= count; ) {
890 if (this.c_str[i] == value[0]) {
894 for (j = 1; equal && j < value.Length; j++) {
895 equal = this.c_str[i + j] == value[j];
896 if (this.c_str[i + j] == value[0] && nexti == 0)
915 public int IndexOfAny (char[] values)
917 return IndexOfAny (values, 0, this.length);
920 public int IndexOfAny (char[] values, int startIndex)
922 return IndexOfAny (values, startIndex, this.length - startIndex);
925 public int IndexOfAny (char[] values, int startIndex, int count)
928 throw new ArgumentNullException ();
930 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
931 throw new ArgumentOutOfRangeException ();
933 for (int i = startIndex; i < startIndex + count; i++) {
934 for (int j = 0; j < strlen (values); j++) {
935 if (this.c_str[i] == values[j])
943 public string Insert (int startIndex, string value)
949 throw new ArgumentNullException ();
951 if (startIndex < 0 || startIndex > this.length)
952 throw new ArgumentOutOfRangeException ();
954 String res = new String (value.length + this.length);
957 for (i = 0; i < startIndex; i++)
958 str[i] = this.c_str[i];
959 for (j = 0; j < value.length; j++)
960 str[i + j] = value.c_str[j];
961 for ( ; i < this.length; i++)
962 str[i + j] = this.c_str[i];
967 [MethodImplAttribute(MethodImplOptions.InternalCall)]
968 public extern static string Intern (string str);
970 [MethodImplAttribute(MethodImplOptions.InternalCall)]
971 public extern static string IsInterned (string str);
973 public static string Join (string separator, string[] value)
975 return Join (separator, value, 0, value.Length);
978 public static string Join (string separator, string[] value, int startIndex, int count)
980 // LAMESPEC: msdn doesn't specify what happens when separator is null
984 if (separator == null || value == null)
985 throw new ArgumentNullException ();
987 if (startIndex + count > value.Length)
988 throw new ArgumentOutOfRangeException ();
991 for (i = startIndex, used = 0; used < count; i++, used++) {
993 len += separator.length;
995 len += value[i].length;
998 // We have no elements to join?
1000 return String.Empty;
1002 String res = new String (len);
1005 for (i = 0; i < value[startIndex].length; i++)
1006 str[i] = value[startIndex][i];
1009 for (j = startIndex + 1; used < count; j++, used++) {
1012 for (k = 0; k < separator.length; k++)
1013 str[i++] = separator.c_str[k];
1014 for (k = 0; k < value[j].length; k++)
1015 str[i++] = value[j].c_str[k];
1021 public int LastIndexOf (char value)
1023 int i = this.length;
1027 for (; i >= 0; i--) {
1028 if (this.c_str[i] == value)
1035 public int LastIndexOf (string value)
1037 return LastIndexOf (value, this.length - 1, this.length);
1040 public int LastIndexOf (char value, int startIndex)
1042 if (startIndex < 0 || startIndex >= this.length)
1043 throw new ArgumentOutOfRangeException ();
1045 for (int i = startIndex; i >= 0; i--) {
1046 if (this.c_str[i] == value)
1053 public int LastIndexOf (string value, int startIndex)
1055 return LastIndexOf (value, startIndex, startIndex + 1);
1058 public int LastIndexOf (char value, int startIndex, int count)
1060 if (startIndex < 0 || count < 0)
1061 throw new ArgumentOutOfRangeException ();
1063 if (startIndex >= this.length || startIndex - count + 1 < 0)
1064 throw new ArgumentOutOfRangeException ();
1066 for (int i = startIndex; i > startIndex - count; i--) {
1067 if (this.c_str[i] == value)
1074 public int LastIndexOf (string value, int startIndex, int count)
1076 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
1077 // but maybe it's the end-position in MS's implementation?
1078 // msdn is unclear on this point. I think this is correct though.
1082 throw new ArgumentNullException ();
1084 if (startIndex < 0 || startIndex >= this.length)
1085 throw new ArgumentOutOfRangeException ();
1087 if (count < 0 || startIndex - count + 1 < 0)
1088 throw new ArgumentOutOfRangeException ();
1090 if (value == String.Empty)
1093 if (startIndex + value.length > this.length) {
1094 /* just a little optimization */
1097 start = this.length - value.length;
1098 count -= startIndex - start;
1102 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1103 len = value.length - 1;
1104 for (i = startIndex; i > startIndex - count; i--) {
1105 if (this.c_str[i + len] == value.c_str[len]) {
1109 for (j = len - 1; equal && j >= 0; j--)
1110 equal = this.c_str[i + j] == value.c_str[j];
1120 public int LastIndexOfAny (char[] values)
1122 return LastIndexOfAny (values, this.length - 1, this.length);
1125 public int LastIndexOfAny (char[] values, int startIndex)
1127 return LastIndexOfAny (values, startIndex, startIndex + 1);
1130 public int LastIndexOfAny (char[] values, int startIndex, int count)
1135 throw new ArgumentNullException ();
1137 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1138 throw new ArgumentOutOfRangeException ();
1140 for (i = startIndex; i > startIndex - count; i--) {
1141 for (int j = 0; j < strlen (values); j++) {
1142 if (this.c_str[i] == values[j])
1150 public string PadLeft (int totalWidth)
1152 return PadLeft (totalWidth, ' ');
1155 public string PadLeft (int totalWidth, char padChar)
1161 throw new ArgumentException ();
1163 str = new char [totalWidth > this.length ? totalWidth : this.length];
1164 for (i = 0; i < totalWidth - this.length; i++)
1167 for (j = 0; j < this.length; i++, j++)
1168 str[i] = this.c_str[j];
1170 return new String (str);
1173 public string PadRight (int totalWidth)
1175 return PadRight (totalWidth, ' ');
1178 public string PadRight (int totalWidth, char padChar)
1184 throw new ArgumentException ();
1186 str = new char [totalWidth > this.length ? totalWidth : this.length];
1187 for (i = 0; i < this.length; i++)
1188 str[i] = this.c_str[i];
1190 for ( ; i < str.Length; i++)
1193 return new String (str);
1196 public string Remove (int startIndex, int count)
1201 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1202 throw new ArgumentOutOfRangeException ();
1204 len = this.length - count;
1206 return String.Empty;
1208 String res = new String (len);
1210 for (i = 0; i < startIndex; i++)
1211 str[i] = this.c_str[i];
1212 for (j = i + count; j < this.length; j++)
1213 str[i++] = this.c_str[j];
1218 public string Replace (char oldChar, char newChar)
1223 String res = new String (length);
1225 for (i = 0; i < this.length; i++) {
1226 if (this.c_str[i] == oldChar)
1229 str[i] = this.c_str[i];
1235 public string Replace (string oldValue, string newValue)
1237 // LAMESPEC: msdn doesn't specify what to do if either args is null
1238 int index, len, i, j;
1241 if (oldValue == null || newValue == null)
1242 throw new ArgumentNullException ();
1244 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1245 index = IndexOf (oldValue, 0);
1247 // This is the easy one ;-)
1248 return Substring (0, this.length);
1251 len = this.length - oldValue.length + newValue.length;
1253 return String.Empty;
1255 String res = new String (len);
1257 for (i = 0; i < index; i++)
1258 str[i] = this.c_str[i];
1259 for (j = 0; j < newValue.length; j++)
1260 str[i++] = newValue[j];
1261 for (j = index + oldValue.length; j < this.length; j++)
1262 str[i++] = this.c_str[j];
1267 private int splitme (char[] separators, int startIndex)
1269 /* this is basically a customized IndexOfAny() for the Split() methods */
1270 for (int i = startIndex; i < this.length; i++) {
1271 if (separators != null) {
1272 foreach (char sep in separators) {
1273 if (this.c_str[i] == sep)
1274 return i - startIndex;
1276 } else if (is_lwsp (this.c_str[i])) {
1277 return i - startIndex;
1284 public string[] Split (params char[] separator)
1288 * @separator: delimiting chars or null to split on whtspc
1290 * Returns: 1. An array consisting of a single
1291 * element (@this) if none of the delimiting
1292 * chars appear in @this. 2. An array of
1293 * substrings which are delimited by one of
1294 * the separator chars. 3. An array of
1295 * substrings separated by whitespace if
1296 * @separator is null. The Empty string should
1297 * be returned wherever 2 delimiting chars are
1300 // FIXME: would using a Queue be better?
1305 list = new ArrayList ();
1306 for (index = 0, len = 0; index < this.length; index += len + 1) {
1307 len = splitme (separator, index);
1308 len = len > -1 ? len : this.length - index;
1310 list.Add (String.Empty);
1315 str = new char [len];
1316 for (i = 0; i < len; i++)
1317 str[i] = this.c_str[index + i];
1319 list.Add (new String (str));
1323 strings = new string [list.Count];
1324 if (list.Count == 1) {
1325 /* special case for an array holding @this */
1328 for (index = 0; index < list.Count; index++)
1329 strings[index] = (string) list[index];
1335 public string[] Split (char[] separator, int maxCount)
1337 // FIXME: what to do if maxCount <= 0?
1338 // FIXME: would using Queue be better than ArrayList?
1341 int index, len, used;
1344 list = new ArrayList ();
1345 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1346 len = splitme (separator, index);
1347 len = len > -1 ? len : this.length - index;
1349 list.Add (String.Empty);
1354 str = new char [len];
1355 for (i = 0; i < len; i++)
1356 str[i] = this.c_str[index + i];
1358 list.Add (new String (str));
1363 /* fit the remaining chunk of the @this into it's own element */
1364 if (index != this.length) {
1368 str = new char [this.length - index];
1369 for (i = index; i < this.length; i++)
1370 str[i - index] = this.c_str[i];
1372 list.Add (new String (str));
1375 strings = new string [list.Count];
1376 if (list.Count == 1) {
1377 /* special case for an array holding @this */
1380 for (index = 0; index < list.Count; index++)
1381 strings[index] = (string) list[index];
1387 public bool StartsWith (string value)
1389 bool startswith = true;
1393 throw new ArgumentNullException ();
1395 if (value.length > this.length)
1398 for (i = 0; i < value.length && startswith; i++)
1399 startswith = startswith && value.c_str[i] == this.c_str[i];
1404 public string Substring (int startIndex)
1409 if (startIndex < 0 || startIndex > this.length)
1410 throw new ArgumentOutOfRangeException ();
1412 len = this.length - startIndex;
1414 return String.Empty;
1415 String res = new String (len);
1417 for (i = startIndex; i < this.length; i++)
1418 str[i - startIndex] = this.c_str[i];
1423 public string Substring (int startIndex, int length)
1428 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1429 throw new ArgumentOutOfRangeException ();
1432 return String.Empty;
1434 String res = new String (length);
1436 for (i = startIndex; i < startIndex + length; i++)
1437 str[i - startIndex] = this.c_str[i];
1443 public bool ToBoolean (IFormatProvider provider)
1445 // FIXME: implement me
1446 throw new NotImplementedException ();
1450 public byte ToByte (IFormatProvider provider)
1452 // FIXME: implement me
1453 throw new NotImplementedException ();
1457 public char ToChar (IFormatProvider provider)
1459 // FIXME: implement me
1460 throw new NotImplementedException ();
1463 public char[] ToCharArray ()
1465 return ToCharArray (0, this.length);
1468 public char[] ToCharArray (int startIndex, int length)
1473 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1474 throw new ArgumentOutOfRangeException ();
1476 chars = new char [length];
1477 for (i = startIndex; i < length; i++)
1478 chars[i - startIndex] = this.c_str[i];
1484 public DateTime ToDateTime (IFormatProvider provider)
1486 // FIXME: implement me
1487 // return new DateTime (0);
1488 throw new NotImplementedException ();
1492 public decimal ToDecimal (IFormatProvider provider)
1494 // FIXME: implement me
1495 throw new NotImplementedException ();
1499 public double ToDouble (IFormatProvider provider)
1501 // FIXME: implement me
1502 throw new NotImplementedException ();
1506 public short ToInt16 (IFormatProvider provider)
1508 // FIXME: implement me
1509 throw new NotImplementedException ();
1513 public int ToInt32 (IFormatProvider provider)
1515 // FIXME: implement me
1516 throw new NotImplementedException ();
1520 public long ToInt64 (IFormatProvider provider)
1522 // FIXME: implement me
1523 throw new NotImplementedException ();
1526 public string ToLower ()
1531 String res = new String (length);
1533 for (i = 0; i < this.length; i++)
1534 str[i] = Char.ToLower (this.c_str[i]);
1540 public string ToLower (CultureInfo culture)
1542 // FIXME: implement me
1543 throw new NotImplementedException ();
1547 [CLSCompliant(false)][MonoTODO]
1548 public sbyte ToSByte (IFormatProvider provider)
1550 // FIXME: implement me
1551 throw new NotImplementedException ();
1555 public float ToSingle (IFormatProvider provider)
1557 // FIXME: implement me
1558 throw new NotImplementedException ();
1561 public override string ToString ()
1567 public string ToString (IFormatProvider format)
1569 // FIXME: implement me
1570 throw new NotImplementedException ();
1574 public object ToType (Type conversionType, IFormatProvider provider)
1576 // FIXME: implement me
1577 throw new NotImplementedException ();
1580 [CLSCompliant(false)][MonoTODO]
1581 public ushort ToUInt16 (IFormatProvider provider)
1583 // FIXME: implement me
1584 throw new NotImplementedException ();
1587 [CLSCompliant(false)][MonoTODO]
1588 public uint ToUInt32 (IFormatProvider provider)
1590 // FIXME: implement me
1591 throw new NotImplementedException ();
1594 [CLSCompliant(false)][MonoTODO]
1595 public ulong ToUInt64 (IFormatProvider provider)
1597 // FIXME: implement me
1598 throw new NotImplementedException ();
1601 public string ToUpper ()
1606 String res = new String (length);
1608 for (i = 0; i < this.length; i++)
1609 str[i] = Char.ToUpper (this.c_str[i]);
1615 public string ToUpper (CultureInfo culture)
1617 // FIXME: implement me
1618 throw new NotImplementedException ();
1621 public string Trim ()
1626 public string Trim (params char[] trimChars)
1632 for (begin = 0; matches && begin < this.length; begin++) {
1633 if (trimChars != null) {
1635 foreach (char c in trimChars) {
1636 matches = this.c_str[begin] == c;
1641 matches = is_lwsp (this.c_str[begin]);
1646 for (end = this.length - 1; matches && end > begin; end--) {
1647 if (trimChars != null) {
1649 foreach (char c in trimChars) {
1650 matches = this.c_str[end] == c;
1655 matches = is_lwsp (this.c_str[end]);
1660 return String.Empty;
1662 return Substring (begin, end - begin);
1665 public string TrimEnd (params char[] trimChars)
1667 bool matches = true;
1670 for (end = this.length; end > 0; end--) {
1671 if (trimChars != null) {
1673 foreach (char c in trimChars) {
1674 matches = this.c_str[end] == c;
1679 matches = is_lwsp (this.c_str[end]);
1684 return String.Empty;
1686 return Substring (0, end);
1689 public string TrimStart (params char[] trimChars)
1691 bool matches = true;
1694 for (begin = 0; matches && begin < this.length; begin++) {
1695 if (trimChars != null) {
1697 foreach (char c in trimChars) {
1698 matches = this.c_str[begin] == c;
1703 matches = is_lwsp (this.c_str[begin]);
1707 if (begin == this.length)
1708 return String.Empty;
1710 return Substring (begin, this.length - begin);
1714 public static bool operator ==(string a, string b)
1716 if ((object)a == null) {
1717 if ((object)b == null)
1721 if ((object)b == null)
1724 if (a.length != b.length)
1728 for (int i = 0; i < l; i++)
1729 if (a.c_str[i] != b.c_str[i])
1735 public static bool operator !=(string a, string b)
1742 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1743 // parses format specifier of form:
1749 // N = argument number (non-negative integer)
1751 n = ParseDecimal (str, ref ptr);
1753 throw new FormatException ("Input string was not in correct format.");
1755 // M = width (non-negative integer)
1757 if (str[ptr] == ',') {
1758 left_align = (str[++ ptr] == '-');
1762 width = ParseDecimal (str, ref ptr);
1764 throw new FormatException ("Input string was not in correct format.");
1771 // F = argument format (string)
1773 if (str[ptr] == ':') {
1775 while (str[ptr] != '}')
1778 format = str.Substring (start, ptr - start);
1783 if (str[ptr ++] != '}')
1784 throw new FormatException ("Input string was not in correct format.");
1786 catch (IndexOutOfRangeException) {
1787 throw new FormatException ("Input string was not in correct format.");
1791 private static int ParseDecimal (string str, ref int ptr) {
1796 if (c < '0' || '9' < c)
1799 n = n * 10 + c - '0';