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 const int StringCompareModeDirect = 0;
273 const int StringCompareModeCaseInsensitive = 1;
274 const int StringCompareModeOrdinal = 2;
276 internal static int _CompareGetLength (string strA, string strB)
278 if ((strA == null) || (strB == null))
281 return Math.Max (strA.Length, strB.Length);
284 internal static int _CompareChar (char chrA, char chrB, CultureInfo culture,
290 case StringCompareModeDirect:
291 // FIXME: We should do a culture based comparision here,
292 // but for the moment let's do it by hand.
293 // In the microsoft runtime, uppercase letters
294 // sort after lowercase letters in the default
296 if (Char.IsUpper (chrA) && Char.IsLower (chrB))
298 else if (Char.IsLower (chrA) && Char.IsUpper (chrB))
300 result = (int) (chrA - chrB);
302 case StringCompareModeCaseInsensitive:
303 result = (int) (Char.ToLower (chrA) - Char.ToLower (chrB));
305 case StringCompareModeOrdinal:
306 result = (int) (tolowerordinal (chrA) - tolowerordinal (chrB));
319 internal static int _Compare (string strA, int indexA, string strB, int indexB,
320 int length, CultureInfo culture,
326 /* When will the hurting stop!?!? */
332 } else if (strB == null)
335 if (length < 0 || indexA < 0 || indexB < 0)
336 throw new ArgumentOutOfRangeException ();
338 if (indexA > strA.Length || indexB > strB.Length)
339 throw new ArgumentOutOfRangeException ();
341 // FIXME: Implement culture
343 throw new NotImplementedException ();
345 for (i = 0; i < length - 1; i++) {
346 if ((indexA+i >= strA.Length) || (indexB+i >= strB.Length))
349 if (_CompareChar (strA[indexA+i], strB[indexB+i], culture, mode) != 0)
353 if (indexA+i >= strA.Length) {
354 if (indexB+i >= strB.Length)
358 } else if (indexB+i >= strB.Length)
361 return _CompareChar (strA[indexA+i], strB[indexB+i], culture, mode);
365 public static int Compare (string strA, string strB)
367 return Compare (strA, strB, false);
370 public static int Compare (string strA, string strB, bool ignoreCase)
372 return Compare (strA, strB, ignoreCase, null);
375 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
377 return Compare (strA, 0, strB, 0,
378 _CompareGetLength (strA, strB),
379 ignoreCase, culture);
382 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
384 return Compare (strA, indexA, strB, indexB, length, false);
387 public static int Compare (string strA, int indexA, string strB, int indexB,
388 int length, bool ignoreCase)
390 return Compare (strA, indexA, strB, indexB, length, ignoreCase, null);
393 public static int Compare (string strA, int indexA, string strB, int indexB,
394 int length, bool ignoreCase, CultureInfo culture)
398 mode = ignoreCase ? StringCompareModeCaseInsensitive :
399 StringCompareModeDirect;
401 return _Compare (strA, indexA, strB, indexB, length, culture, mode);
404 public static int CompareOrdinal (string strA, string strB)
406 return CompareOrdinal (strA, 0, strB, 0, _CompareGetLength (strA, strB));
409 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
412 return _Compare (strA, indexA, strB, indexB, length, null,
413 StringCompareModeOrdinal);
416 public int CompareTo (object obj)
418 return Compare (this, obj == null ? null : obj.ToString ());
421 public int CompareTo (string str)
423 return Compare (this, str);
426 public static string Concat (object arg)
428 return arg != null ? arg.ToString () : String.Empty;
431 public static string Concat (params object[] args)
438 throw new ArgumentNullException ();
440 strings = new string [args.Length];
443 foreach (object arg in args) {
444 /* use Empty for each null argument */
446 strings[i] = String.Empty;
448 strings[i] = arg.ToString ();
449 len += strings[i].length;
456 String res = new String (len);
459 for (int j = 0; j < strings.Length; j++)
460 for (int k = 0; k < strings[j].length; k++)
461 str[i++] = strings[j].c_str[k];
466 public static string Concat (params string[] values)
472 throw new ArgumentNullException ();
475 foreach (string value in values)
476 len += value != null ? value.Length : 0;
481 String res = new String (len);
484 foreach (string value in values) {
488 for (int j = 0; j < value.length; j++)
489 str[i++] = value.c_str[j];
495 public static string Concat (object arg0, object arg1)
497 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
498 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
500 return Concat (str0, str1);
503 public static string Concat (string str0, string str1)
513 len = str0.length + str1.length;
517 String res = new String (len);
520 for (i = 0; i < str0.length; i++)
521 concat[i] = str0.c_str[i];
522 for (j = 0 ; j < str1.length; j++)
523 concat[i + j] = str1.c_str[j];
528 public static string Concat (object arg0, object arg1, object arg2)
530 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
531 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
532 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
534 return Concat (str0, str1, str2);
537 public static string Concat (string str0, string str1, string str2)
549 len = str0.length + str1.length + str2.length;
553 String res = new String (len);
556 for (i = 0; i < str0.length; i++)
557 concat[i] = str0.c_str[i];
558 for (j = 0; j < str1.length; j++)
559 concat[i + j] = str1.c_str[j];
560 for (k = 0; k < str2.length; k++)
561 concat[i + j + k] = str2.c_str[k];
566 public static string Concat (string str0, string str1, string str2, string str3)
580 len = str0.length + str1.length + str2.length + str3.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];
590 for (k = 0; k < str2.length; k++)
591 concat[i + j + k] = str2.c_str[k];
592 for (l = 0; l < str3.length; l++)
593 concat[i + j + k + l] = str3.c_str[l];
598 public static string Copy (string str)
600 // FIXME: how do I *copy* a string if I can only have 1 of each?
602 throw new ArgumentNullException ();
607 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
609 // LAMESPEC: should I null-terminate?
612 if (destination == null)
613 throw new ArgumentNullException ();
615 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
616 throw new ArgumentOutOfRangeException ();
618 if (sourceIndex + count > this.length)
619 throw new ArgumentOutOfRangeException ();
621 if (destinationIndex + count > destination.Length)
622 throw new ArgumentOutOfRangeException ();
624 for (i = 0; i < count; i++)
625 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
628 public bool EndsWith (string value)
630 bool endswith = true;
634 throw new ArgumentNullException ();
636 start = this.length - value.length;
640 for (i = start; i < this.length && endswith; i++)
641 endswith = this.c_str[i] == value.c_str[i - start];
646 public override bool Equals (object obj)
648 if (!(obj is String))
651 return this == (String) obj;
654 public bool Equals (string value)
656 return this == value;
659 public static bool Equals (string a, string b)
664 public static string Format (string format, object arg0) {
665 return Format (null, format, new object[] { arg0 });
668 public static string Format (string format, object arg0, object arg1) {
669 return Format (null, format, new object[] { arg0, arg1 });
672 public static string Format (string format, object arg0, object arg1, object arg2) {
673 return Format (null, format, new object[] { arg0, arg1, arg2 });
676 public static string Format (string format, params object[] args) {
677 return Format (null, format, args);
680 public static string Format (IFormatProvider provider, string format, params object[] args) {
681 if (format == null || args == null)
682 throw new ArgumentNullException ();
684 StringBuilder result = new StringBuilder ();
688 while (ptr < format.Length) {
689 char c = format[ptr ++];
692 result.Append (format, start, ptr - start - 1);
694 // check for escaped open bracket
696 if (format[ptr] == '{') {
707 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
708 if (n >= args.Length)
709 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
713 object arg = args[n];
718 else if (arg is IFormattable)
719 str = ((IFormattable)arg).ToString (arg_format, provider);
721 str = arg.ToString ();
723 // pad formatted string and append to result
725 if (width > str.Length) {
726 string pad = new String (' ', width - str.Length);
742 else if (c == '}' && format[ptr] == '}') {
743 result.Append (format, start, ptr - start - 1);
748 if (start < format.Length)
749 result.Append (format.Substring (start));
751 return result.ToString ();
754 public CharEnumerator GetEnumerator ()
756 return new CharEnumerator (this);
759 IEnumerator IEnumerable.GetEnumerator ()
761 return new CharEnumerator (this);
764 public override int GetHashCode ()
768 for (i = 0; i < length; ++i)
769 h = (h << 5) - h + c_str [i];
773 public TypeCode GetTypeCode ()
775 return TypeCode.String;
778 public int IndexOf (char value)
780 return IndexOf (value, 0, this.length);
783 public int IndexOf (string value)
785 return IndexOf (value, 0, this.length);
788 public int IndexOf (char value, int startIndex)
790 return IndexOf (value, startIndex, this.length - startIndex);
793 public int IndexOf (string value, int startIndex)
795 return IndexOf (value, startIndex, this.length - startIndex);
798 public int IndexOf (char value, int startIndex, int count)
802 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
803 throw new ArgumentOutOfRangeException ();
805 for (i = startIndex; i - startIndex < count; i++)
806 if (this.c_str[i] == value)
812 public int IndexOf (string value, int startIndex, int count)
815 throw new ArgumentNullException ();
817 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
818 throw new ArgumentOutOfRangeException ();
821 return BoyerMoore (this.c_str, value, startIndex, count);
824 for (i = startIndex; i - startIndex + value.length <= count; ) {
825 if (this.c_str[i] == value.c_str [0]) {
829 for (j = 1; equal && j < value.length; j++) {
830 equal = this.c_str[i + j] == value.c_str [j];
831 if (this.c_str [i + j] == value.c_str [0] && nexti == 0)
849 public int IndexOfAny (char[] values)
851 return IndexOfAny (values, 0, this.length);
854 public int IndexOfAny (char[] values, int startIndex)
856 return IndexOfAny (values, startIndex, this.length - startIndex);
859 public int IndexOfAny (char[] values, int startIndex, int count)
862 throw new ArgumentNullException ();
864 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
865 throw new ArgumentOutOfRangeException ();
867 for (int i = startIndex; i < startIndex + count; i++) {
868 for (int j = 0; j < strlen (values); j++) {
869 if (this.c_str[i] == values[j])
877 public string Insert (int startIndex, string value)
883 throw new ArgumentNullException ();
885 if (startIndex < 0 || startIndex > this.length)
886 throw new ArgumentOutOfRangeException ();
888 String res = new String (value.length + this.length);
891 for (i = 0; i < startIndex; i++)
892 str[i] = this.c_str[i];
893 for (j = 0; j < value.length; j++)
894 str[i + j] = value.c_str[j];
895 for ( ; i < this.length; i++)
896 str[i + j] = this.c_str[i];
901 [MethodImplAttribute(MethodImplOptions.InternalCall)]
902 internal extern static string _Intern (string str);
904 public static string Intern (string str)
907 throw new ArgumentNullException ();
909 return _Intern (str);
912 [MethodImplAttribute(MethodImplOptions.InternalCall)]
913 internal extern static string _IsInterned (string str);
915 public static string IsInterned (string str)
918 throw new ArgumentNullException ();
920 return _IsInterned (str);
923 public static string Join (string separator, string[] value)
926 throw new ArgumentNullException ();
928 return Join (separator, value, 0, value.Length);
931 public static string Join (string separator, string[] value, int startIndex, int count)
933 // LAMESPEC: msdn doesn't specify what happens when separator is null
937 if (separator == null || value == null)
938 throw new ArgumentNullException ();
940 if (startIndex + count > value.Length)
941 throw new ArgumentOutOfRangeException ();
944 for (i = startIndex, used = 0; used < count; i++, used++) {
946 len += separator.length;
948 len += value[i].length;
951 // We have no elements to join?
955 String res = new String (len);
958 for (i = 0; i < value[startIndex].length; i++)
959 str[i] = value[startIndex][i];
962 for (j = startIndex + 1; used < count; j++, used++) {
965 for (k = 0; k < separator.length; k++)
966 str[i++] = separator.c_str[k];
967 for (k = 0; k < value[j].length; k++)
968 str[i++] = value[j].c_str[k];
974 public int LastIndexOf (char value)
980 for (; i >= 0; i--) {
981 if (this.c_str[i] == value)
988 public int LastIndexOf (string value)
991 throw new ArgumentNullException ();
992 if (value.length == 0)
994 if (this.length == 0)
997 return LastIndexOf (value, this.length - 1, this.length);
1000 public int LastIndexOf (char value, int startIndex)
1002 if (startIndex < 0 || startIndex >= this.length)
1003 throw new ArgumentOutOfRangeException ();
1005 for (int i = startIndex; i >= 0; i--) {
1006 if (this.c_str[i] == value)
1013 public int LastIndexOf (string value, int startIndex)
1015 return LastIndexOf (value, startIndex, startIndex + 1);
1018 public int LastIndexOf (char value, int startIndex, int count)
1020 if (startIndex < 0 || count < 0)
1021 throw new ArgumentOutOfRangeException ();
1023 if (startIndex >= this.length || startIndex - count + 1 < 0)
1024 throw new ArgumentOutOfRangeException ();
1026 for (int i = startIndex; i > startIndex - count; i--) {
1027 if (this.c_str[i] == value)
1034 public int LastIndexOf (string value, int startIndex, int count)
1036 // startIndex points to the end of value, ie. we're searching backwards.
1040 throw new ArgumentNullException ();
1042 if (startIndex < 0 || startIndex > this.length)
1043 throw new ArgumentOutOfRangeException ();
1045 if (count < 0 || startIndex - count + 1 < 0)
1046 throw new ArgumentOutOfRangeException ();
1048 if (value.length > startIndex)
1051 if (value == String.Empty)
1054 if (startIndex == this.length)
1057 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1058 len = value.length - 1;
1059 for (i = startIndex; i > startIndex - count; i--) {
1061 if (this.c_str[i] == value.c_str[len]) {
1065 for (j = 0; equal && j < len; j++)
1066 equal = this.c_str[i - j] == value.c_str[len - j];
1076 public int LastIndexOfAny (char[] values)
1078 return LastIndexOfAny (values, this.length - 1, this.length);
1081 public int LastIndexOfAny (char[] values, int startIndex)
1083 return LastIndexOfAny (values, startIndex, startIndex + 1);
1086 public int LastIndexOfAny (char[] values, int startIndex, int count)
1091 throw new ArgumentNullException ();
1093 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1094 throw new ArgumentOutOfRangeException ();
1096 for (i = startIndex; i > startIndex - count; i--) {
1097 for (int j = 0; j < strlen (values); j++) {
1098 if (this.c_str[i] == values[j])
1106 public string PadLeft (int totalWidth)
1108 return PadLeft (totalWidth, ' ');
1111 public string PadLeft (int totalWidth, char padChar)
1117 throw new ArgumentException ();
1119 str = new char [totalWidth > this.length ? totalWidth : this.length];
1120 for (i = 0; i < totalWidth - this.length; i++)
1123 for (j = 0; j < this.length; i++, j++)
1124 str[i] = this.c_str[j];
1126 return new String (str);
1129 public string PadRight (int totalWidth)
1131 return PadRight (totalWidth, ' ');
1134 public string PadRight (int totalWidth, char padChar)
1140 throw new ArgumentException ();
1142 str = new char [totalWidth > this.length ? totalWidth : this.length];
1143 for (i = 0; i < this.length; i++)
1144 str[i] = this.c_str[i];
1146 for ( ; i < str.Length; i++)
1149 return new String (str);
1152 public string Remove (int startIndex, int count)
1157 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1158 throw new ArgumentOutOfRangeException ();
1160 len = this.length - count;
1162 return String.Empty;
1164 String res = new String (len);
1166 for (i = 0; i < startIndex; i++)
1167 str[i] = this.c_str[i];
1168 for (j = i + count; j < this.length; j++)
1169 str[i++] = this.c_str[j];
1174 public string Replace (char oldChar, char newChar)
1179 String res = new String (length);
1181 for (i = 0; i < this.length; i++) {
1182 if (this.c_str[i] == oldChar)
1185 str[i] = this.c_str[i];
1191 public string Replace (string oldValue, string newValue)
1193 // If newValue is null, oldValue is removed.
1194 int index, len, i, j, newlen;
1198 if (oldValue == null)
1199 throw new ArgumentNullException ();
1201 thestring = Substring (0, this.length);
1204 // Runs until all occurences of oldValue have been replaced.
1206 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1207 index = thestring.IndexOf (oldValue, index);
1212 newlen = (newValue == null) ? 0 : newValue.length;
1213 len = thestring.length - oldValue.length + newlen;
1216 return String.Empty;
1218 String res = new String (len);
1220 for (i = 0; i < index; i++)
1221 str[i] = thestring.c_str[i];
1222 for (j = 0; j < newlen; j++)
1223 str[i++] = newValue[j];
1224 for (j = index + oldValue.length; j < thestring.length; j++)
1225 str[i++] = thestring.c_str[j];
1227 // Increment index, we're already done replacing until this index.
1233 private int splitme (char[] separators, int startIndex)
1235 /* this is basically a customized IndexOfAny() for the Split() methods */
1236 for (int i = startIndex; i < this.length; i++) {
1237 if (separators != null) {
1238 foreach (char sep in separators) {
1239 if (this.c_str[i] == sep)
1240 return i - startIndex;
1242 } else if (is_lwsp (this.c_str[i])) {
1243 return i - startIndex;
1250 public string[] Split (params char[] separator)
1254 * @separator: delimiting chars or null to split on whtspc
1256 * Returns: 1. An array consisting of a single
1257 * element (@this) if none of the delimiting
1258 * chars appear in @this. 2. An array of
1259 * substrings which are delimited by one of
1260 * the separator chars. 3. An array of
1261 * substrings separated by whitespace if
1262 * @separator is null. The Empty string should
1263 * be returned wherever 2 delimiting chars are
1266 // FIXME: would using a Queue be better?
1271 list = new ArrayList ();
1272 for (index = 0, len = 0; index < this.length; index += len + 1) {
1273 len = splitme (separator, index);
1274 len = len > -1 ? len : this.length - index;
1276 list.Add (String.Empty);
1281 str = new char [len];
1282 for (i = 0; i < len; i++)
1283 str[i] = this.c_str[index + i];
1285 list.Add (new String (str));
1289 strings = new string [list.Count];
1290 if (list.Count == 1) {
1291 /* special case for an array holding @this */
1294 for (index = 0; index < list.Count; index++)
1295 strings[index] = (string) list[index];
1301 public string[] Split (char[] separator, int maxCount)
1303 // FIXME: would using Queue be better than ArrayList?
1306 int index, len, used;
1309 return new string[0];
1310 else if (maxCount < 0)
1311 throw new ArgumentOutOfRangeException ();
1314 list = new ArrayList ();
1315 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1316 len = splitme (separator, index);
1317 len = len > -1 ? len : this.length - index;
1319 list.Add (String.Empty);
1324 str = new char [len];
1325 for (i = 0; i < len; i++)
1326 str[i] = this.c_str[index + i];
1328 list.Add (new String (str));
1333 /* fit the remaining chunk of the @this into it's own element */
1334 if (index <= this.length)
1339 str = new char [this.length - index];
1340 for (i = index; i < this.length; i++)
1341 str[i - index] = this.c_str[i];
1343 // maxCount cannot be zero if we reach this point and this means that
1344 // index can't be zero either.
1345 if (used == maxCount)
1346 list[used-1] += this.c_str[index-1] + new String (str);
1348 list.Add (new String (str));
1351 strings = new string [list.Count];
1352 if (list.Count == 1) {
1353 /* special case for an array holding @this */
1356 for (index = 0; index < list.Count; index++)
1357 strings[index] = (string) list[index];
1363 public bool StartsWith (string value)
1365 bool startswith = true;
1369 throw new ArgumentNullException ();
1371 if (value.length > this.length)
1374 for (i = 0; i < value.length && startswith; i++)
1375 startswith = startswith && value.c_str[i] == this.c_str[i];
1380 public string Substring (int startIndex)
1385 if (startIndex < 0 || startIndex > this.length)
1386 throw new ArgumentOutOfRangeException ();
1388 len = this.length - startIndex;
1390 return String.Empty;
1391 String res = new String (len);
1393 for (i = startIndex; i < this.length; i++)
1394 str[i - startIndex] = this.c_str[i];
1399 public string Substring (int startIndex, int length)
1404 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1405 throw new ArgumentOutOfRangeException ();
1408 return String.Empty;
1410 String res = new String (length);
1412 for (i = startIndex; i < startIndex + length; i++)
1413 str[i - startIndex] = this.c_str[i];
1418 bool IConvertible.ToBoolean (IFormatProvider provider)
1420 return Convert.ToBoolean (this);
1423 byte IConvertible.ToByte (IFormatProvider provider)
1425 return Convert.ToByte (this);
1428 char IConvertible.ToChar (IFormatProvider provider)
1430 return Convert.ToChar (this);
1433 public char[] ToCharArray ()
1435 return ToCharArray (0, this.length);
1438 public char[] ToCharArray (int startIndex, int length)
1443 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1444 throw new ArgumentOutOfRangeException ();
1446 chars = new char [length];
1447 for (i = startIndex; i < length; i++)
1448 chars[i - startIndex] = this.c_str[i];
1453 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1455 return Convert.ToDateTime (this);
1458 decimal IConvertible.ToDecimal (IFormatProvider provider)
1460 return Convert.ToDecimal (this);
1463 double IConvertible.ToDouble (IFormatProvider provider)
1465 return Convert.ToDouble (this);
1468 short IConvertible.ToInt16 (IFormatProvider provider)
1470 return Convert.ToInt16 (this);
1473 int IConvertible.ToInt32 (IFormatProvider provider)
1475 return Convert.ToInt32 (this);
1478 long IConvertible.ToInt64 (IFormatProvider provider)
1480 return Convert.ToInt64 (this);
1483 public string ToLower ()
1488 String res = new String (length);
1490 for (i = 0; i < this.length; i++)
1491 str[i] = Char.ToLower (this.c_str[i]);
1497 public string ToLower (CultureInfo culture)
1499 // FIXME: implement me
1500 throw new NotImplementedException ();
1504 [CLSCompliant(false)]
1505 sbyte IConvertible.ToSByte (IFormatProvider provider)
1507 return Convert.ToSByte (this);
1510 float IConvertible.ToSingle (IFormatProvider provider)
1512 return Convert.ToSingle (this);
1515 public override string ToString ()
1520 string IConvertible.ToString (IFormatProvider format)
1525 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1527 return Convert.ToType (this, conversionType, provider);
1530 [CLSCompliant(false)]
1531 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1533 return Convert.ToUInt16 (this);
1536 [CLSCompliant(false)]
1537 uint IConvertible.ToUInt32 (IFormatProvider provider)
1539 return Convert.ToUInt32 (this);
1542 [CLSCompliant(false)]
1543 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1545 return Convert.ToUInt64 (this);
1548 public string ToUpper ()
1553 String res = new String (length);
1555 for (i = 0; i < this.length; i++)
1556 str[i] = Char.ToUpper (this.c_str[i]);
1562 public string ToUpper (CultureInfo culture)
1564 // FIXME: implement me
1565 throw new NotImplementedException ();
1568 public string Trim ()
1573 public string Trim (params char[] trimChars)
1576 bool matches = false;
1578 for (begin = 0; begin < this.length; begin++) {
1579 if (trimChars != null) {
1581 foreach (char c in trimChars) {
1582 matches = this.c_str[begin] == c;
1589 matches = is_lwsp (this.c_str[begin]);
1596 for (end = this.length - 1; end > begin; end--) {
1597 if (trimChars != null) {
1599 foreach (char c in trimChars) {
1600 matches = this.c_str[end] == c;
1607 matches = is_lwsp (this.c_str[end]);
1616 return String.Empty;
1618 return Substring (begin, end - begin);
1621 public string TrimEnd (params char[] trimChars)
1623 bool matches = true;
1626 for (end = this.length - 1; matches && end > 0; end--) {
1628 if (trimChars != null) {
1630 foreach (char c in trimChars) {
1631 matches = this.c_str[end] == c;
1636 matches = is_lwsp (this.c_str[end]);
1640 return Substring (0, end+1);
1644 return String.Empty;
1646 return Substring (0, end);
1649 public string TrimStart (params char[] trimChars)
1651 bool matches = true;
1654 for (begin = 0; matches && begin < this.length; begin++) {
1655 if (trimChars != null) {
1657 foreach (char c in trimChars) {
1658 matches = this.c_str[begin] == c;
1663 matches = is_lwsp (this.c_str[begin]);
1667 return Substring (begin, this.length - begin);
1670 return String.Empty;
1674 public static bool operator ==(string a, string b)
1676 if ((object)a == null) {
1677 if ((object)b == null)
1681 if ((object)b == null)
1684 if (a.length != b.length)
1688 for (int i = 0; i < l; i++)
1689 if (a.c_str[i] != b.c_str[i])
1695 public static bool operator !=(string a, string b)
1702 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1703 // parses format specifier of form:
1709 // N = argument number (non-negative integer)
1711 n = ParseDecimal (str, ref ptr);
1713 throw new FormatException ("Input string was not in correct format.");
1715 // M = width (non-negative integer)
1717 if (str[ptr] == ',') {
1718 left_align = (str[++ ptr] == '-');
1722 width = ParseDecimal (str, ref ptr);
1724 throw new FormatException ("Input string was not in correct format.");
1731 // F = argument format (string)
1733 if (str[ptr] == ':') {
1735 while (str[ptr] != '}')
1738 format = str.Substring (start, ptr - start);
1743 if (str[ptr ++] != '}')
1744 throw new FormatException ("Input string was not in correct format.");
1746 catch (IndexOutOfRangeException) {
1747 throw new FormatException ("Input string was not in correct format.");
1751 private static int ParseDecimal (string str, ref int ptr) {
1756 if (c < '0' || '9' < c)
1759 n = n * 10 + c - '0';