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 internal enum _StringCompareMode {
274 CompareCaseInsensitive,
278 internal static int _CompareGetLength (string strA, string strB)
280 if ((strA == null) || (strB == null))
283 return Math.Max (strA.Length, strB.Length);
286 internal static int _CompareChar (char chrA, char chrB, CultureInfo culture,
287 _StringCompareMode mode)
292 case _StringCompareMode.CompareDirect:
293 // FIXME: We should do a culture based comparision here,
294 // but for the moment let's do it by hand.
295 // In the microsoft runtime, uppercase letters
296 // sort after lowercase letters in the default
298 if (Char.IsUpper (chrA) && Char.IsLower (chrB))
300 else if (Char.IsLower (chrA) && Char.IsUpper (chrB))
302 result = (int) (chrA - chrB);
304 case _StringCompareMode.CompareCaseInsensitive:
305 result = (int) (Char.ToLower (chrA) - Char.ToLower (chrB));
307 case _StringCompareMode.CompareOrdinal:
308 result = (int) (tolowerordinal (chrA) - tolowerordinal (chrB));
320 internal static int _Compare (string strA, int indexA, string strB, int indexB,
321 int length, CultureInfo culture,
322 _StringCompareMode mode)
327 /* When will the hurting stop!?!? */
333 } else if (strB == null)
336 if (length < 0 || indexA < 0 || indexB < 0)
337 throw new ArgumentOutOfRangeException ();
339 if (indexA > strA.Length || indexB > strB.Length)
340 throw new ArgumentOutOfRangeException ();
342 // FIXME: Implement culture
344 throw new NotImplementedException ();
346 for (i = 0; i < length - 1; i++) {
347 if ((indexA+i >= strA.Length) || (indexB+i >= strB.Length))
350 if (_CompareChar (strA[indexA+i], strB[indexB+i], culture, mode) != 0)
354 if (indexA+i >= strA.Length) {
355 if (indexB+i >= strB.Length)
359 } else if (indexB+i >= strB.Length)
362 return _CompareChar (strA[indexA+i], strB[indexB+i], culture, mode);
366 public static int Compare (string strA, string strB)
368 return Compare (strA, strB, false);
371 public static int Compare (string strA, string strB, bool ignoreCase)
373 return Compare (strA, strB, ignoreCase, null);
376 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
378 return Compare (strA, 0, strB, 0,
379 _CompareGetLength (strA, strB),
380 ignoreCase, culture);
383 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
385 return Compare (strA, indexA, strB, indexB, length, false);
388 public static int Compare (string strA, int indexA, string strB, int indexB,
389 int length, bool ignoreCase)
391 return Compare (strA, indexA, strB, indexB, length, ignoreCase, null);
394 public static int Compare (string strA, int indexA, string strB, int indexB,
395 int length, bool ignoreCase, CultureInfo culture)
397 _StringCompareMode mode;
399 mode = ignoreCase ? _StringCompareMode.CompareCaseInsensitive :
400 _StringCompareMode.CompareDirect;
402 return _Compare (strA, indexA, strB, indexB, length, culture, mode);
405 public static int CompareOrdinal (string strA, string strB)
407 return CompareOrdinal (strA, 0, strB, 0, _CompareGetLength (strA, strB));
410 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
413 return _Compare (strA, indexA, strB, indexB, length, null,
414 _StringCompareMode.CompareOrdinal);
417 public int CompareTo (object obj)
419 return Compare (this, obj == null ? null : obj.ToString ());
422 public int CompareTo (string str)
424 return Compare (this, str);
427 public static string Concat (object arg)
429 return arg != null ? arg.ToString () : String.Empty;
432 public static string Concat (params object[] args)
439 throw new ArgumentNullException ();
441 strings = new string [args.Length];
444 foreach (object arg in args) {
445 /* use Empty for each null argument */
447 strings[i] = String.Empty;
449 strings[i] = arg.ToString ();
450 len += strings[i].length;
457 String res = new String (len);
460 for (int j = 0; j < strings.Length; j++)
461 for (int k = 0; k < strings[j].length; k++)
462 str[i++] = strings[j].c_str[k];
467 public static string Concat (params string[] values)
473 throw new ArgumentNullException ();
476 foreach (string value in values)
477 len += value != null ? value.Length : 0;
482 String res = new String (len);
485 foreach (string value in values) {
489 for (int j = 0; j < value.length; j++)
490 str[i++] = value.c_str[j];
496 public static string Concat (object arg0, object arg1)
498 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
499 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
501 return Concat (str0, str1);
504 public static string Concat (string str0, string str1)
514 len = str0.length + str1.length;
518 String res = new String (len);
521 for (i = 0; i < str0.length; i++)
522 concat[i] = str0.c_str[i];
523 for (j = 0 ; j < str1.length; j++)
524 concat[i + j] = str1.c_str[j];
529 public static string Concat (object arg0, object arg1, object arg2)
531 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
532 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
533 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
535 return Concat (str0, str1, str2);
538 public static string Concat (string str0, string str1, string str2)
550 len = str0.length + str1.length + str2.length;
554 String res = new String (len);
557 for (i = 0; i < str0.length; i++)
558 concat[i] = str0.c_str[i];
559 for (j = 0; j < str1.length; j++)
560 concat[i + j] = str1.c_str[j];
561 for (k = 0; k < str2.length; k++)
562 concat[i + j + k] = str2.c_str[k];
567 public static string Concat (string str0, string str1, string str2, string str3)
581 len = str0.length + str1.length + str2.length + str3.length;
584 String res = new String (len);
587 for (i = 0; i < str0.length; i++)
588 concat[i] = str0.c_str[i];
589 for (j = 0; j < str1.length; j++)
590 concat[i + j] = str1.c_str[j];
591 for (k = 0; k < str2.length; k++)
592 concat[i + j + k] = str2.c_str[k];
593 for (l = 0; l < str3.length; l++)
594 concat[i + j + k + l] = str3.c_str[l];
599 public static string Copy (string str)
601 // FIXME: how do I *copy* a string if I can only have 1 of each?
603 throw new ArgumentNullException ();
608 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
610 // LAMESPEC: should I null-terminate?
613 if (destination == null)
614 throw new ArgumentNullException ();
616 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
617 throw new ArgumentOutOfRangeException ();
619 if (sourceIndex + count > this.length)
620 throw new ArgumentOutOfRangeException ();
622 if (destinationIndex + count > destination.Length)
623 throw new ArgumentOutOfRangeException ();
625 for (i = 0; i < count; i++)
626 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
629 public bool EndsWith (string value)
631 bool endswith = true;
635 throw new ArgumentNullException ();
637 start = this.length - value.length;
641 for (i = start; i < this.length && endswith; i++)
642 endswith = this.c_str[i] == value.c_str[i - start];
647 public override bool Equals (object obj)
649 if (!(obj is String))
652 return this == (String) obj;
655 public bool Equals (string value)
657 return this == value;
660 public static bool Equals (string a, string b)
665 public static string Format (string format, object arg0) {
666 return Format (null, format, new object[] { arg0 });
669 public static string Format (string format, object arg0, object arg1) {
670 return Format (null, format, new object[] { arg0, arg1 });
673 public static string Format (string format, object arg0, object arg1, object arg2) {
674 return Format (null, format, new object[] { arg0, arg1, arg2 });
677 public static string Format (string format, params object[] args) {
678 return Format (null, format, args);
681 public static string Format (IFormatProvider provider, string format, params object[] args) {
682 if (format == null || args == null)
683 throw new ArgumentNullException ();
685 StringBuilder result = new StringBuilder ();
689 while (ptr < format.Length) {
690 char c = format[ptr ++];
693 result.Append (format, start, ptr - start - 1);
695 // check for escaped open bracket
697 if (format[ptr] == '{') {
708 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
709 if (n >= args.Length)
710 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
714 object arg = args[n];
719 else if (arg is IFormattable)
720 str = ((IFormattable)arg).ToString (arg_format, provider);
722 str = arg.ToString ();
724 // pad formatted string and append to result
726 if (width > str.Length) {
727 string pad = new String (' ', width - str.Length);
743 else if (c == '}' && format[ptr] == '}') {
744 result.Append (format, start, ptr - start - 1);
749 if (start < format.Length)
750 result.Append (format.Substring (start));
752 return result.ToString ();
755 public CharEnumerator GetEnumerator ()
757 return new CharEnumerator (this);
760 IEnumerator IEnumerable.GetEnumerator ()
762 return new CharEnumerator (this);
765 public override int GetHashCode ()
769 for (i = 0; i < length; ++i)
770 h = (h << 5) - h + c_str [i];
774 public TypeCode GetTypeCode ()
776 return TypeCode.String;
779 public int IndexOf (char value)
781 return IndexOf (value, 0, this.length);
784 public int IndexOf (string value)
786 return IndexOf (value, 0, this.length);
789 public int IndexOf (char value, int startIndex)
791 return IndexOf (value, startIndex, this.length - startIndex);
794 public int IndexOf (string value, int startIndex)
796 return IndexOf (value, startIndex, this.length - startIndex);
799 public int IndexOf (char value, int startIndex, int count)
803 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
804 throw new ArgumentOutOfRangeException ();
806 for (i = startIndex; i - startIndex < count; i++)
807 if (this.c_str[i] == value)
813 public int IndexOf (string value, int startIndex, int count)
816 throw new ArgumentNullException ();
818 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
819 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[0]) {
829 for (j = 1; equal && j < value.Length; j++) {
830 equal = this.c_str[i + j] == value[j];
831 if (this.c_str[i + j] == value[0] && nexti == 0)
850 public int IndexOfAny (char[] values)
852 return IndexOfAny (values, 0, this.length);
855 public int IndexOfAny (char[] values, int startIndex)
857 return IndexOfAny (values, startIndex, this.length - startIndex);
860 public int IndexOfAny (char[] values, int startIndex, int count)
863 throw new ArgumentNullException ();
865 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
866 throw new ArgumentOutOfRangeException ();
868 for (int i = startIndex; i < startIndex + count; i++) {
869 for (int j = 0; j < strlen (values); j++) {
870 if (this.c_str[i] == values[j])
878 public string Insert (int startIndex, string value)
884 throw new ArgumentNullException ();
886 if (startIndex < 0 || startIndex > this.length)
887 throw new ArgumentOutOfRangeException ();
889 String res = new String (value.length + this.length);
892 for (i = 0; i < startIndex; i++)
893 str[i] = this.c_str[i];
894 for (j = 0; j < value.length; j++)
895 str[i + j] = value.c_str[j];
896 for ( ; i < this.length; i++)
897 str[i + j] = this.c_str[i];
902 [MethodImplAttribute(MethodImplOptions.InternalCall)]
903 public extern static string Intern (string str);
905 [MethodImplAttribute(MethodImplOptions.InternalCall)]
906 public extern static string IsInterned (string str);
908 public static string Join (string separator, string[] value)
910 return Join (separator, value, 0, value.Length);
913 public static string Join (string separator, string[] value, int startIndex, int count)
915 // LAMESPEC: msdn doesn't specify what happens when separator is null
919 if (separator == null || value == null)
920 throw new ArgumentNullException ();
922 if (startIndex + count > value.Length)
923 throw new ArgumentOutOfRangeException ();
926 for (i = startIndex, used = 0; used < count; i++, used++) {
928 len += separator.length;
930 len += value[i].length;
933 // We have no elements to join?
937 String res = new String (len);
940 for (i = 0; i < value[startIndex].length; i++)
941 str[i] = value[startIndex][i];
944 for (j = startIndex + 1; used < count; j++, used++) {
947 for (k = 0; k < separator.length; k++)
948 str[i++] = separator.c_str[k];
949 for (k = 0; k < value[j].length; k++)
950 str[i++] = value[j].c_str[k];
956 public int LastIndexOf (char value)
962 for (; i >= 0; i--) {
963 if (this.c_str[i] == value)
970 public int LastIndexOf (string value)
972 return LastIndexOf (value, this.length - 1, this.length);
975 public int LastIndexOf (char value, int startIndex)
977 if (startIndex < 0 || startIndex >= this.length)
978 throw new ArgumentOutOfRangeException ();
980 for (int i = startIndex; i >= 0; i--) {
981 if (this.c_str[i] == value)
988 public int LastIndexOf (string value, int startIndex)
990 return LastIndexOf (value, startIndex, startIndex + 1);
993 public int LastIndexOf (char value, int startIndex, int count)
995 if (startIndex < 0 || count < 0)
996 throw new ArgumentOutOfRangeException ();
998 if (startIndex >= this.length || startIndex - count + 1 < 0)
999 throw new ArgumentOutOfRangeException ();
1001 for (int i = startIndex; i > startIndex - count; i--) {
1002 if (this.c_str[i] == value)
1009 public int LastIndexOf (string value, int startIndex, int count)
1011 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
1012 // but maybe it's the end-position in MS's implementation?
1013 // msdn is unclear on this point. I think this is correct though.
1017 throw new ArgumentNullException ();
1019 if (startIndex < 0 || startIndex >= this.length)
1020 throw new ArgumentOutOfRangeException ();
1022 if (count < 0 || startIndex - count + 1 < 0)
1023 throw new ArgumentOutOfRangeException ();
1025 if (value == String.Empty)
1028 if (startIndex + value.length > this.length) {
1029 /* just a little optimization */
1032 start = this.length - value.length;
1033 count -= startIndex - start;
1037 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1038 len = value.length - 1;
1039 for (i = startIndex; i > startIndex - count; i--) {
1040 if (this.c_str[i + len] == value.c_str[len]) {
1044 for (j = len - 1; equal && j >= 0; j--)
1045 equal = this.c_str[i + j] == value.c_str[j];
1055 public int LastIndexOfAny (char[] values)
1057 return LastIndexOfAny (values, this.length - 1, this.length);
1060 public int LastIndexOfAny (char[] values, int startIndex)
1062 return LastIndexOfAny (values, startIndex, startIndex + 1);
1065 public int LastIndexOfAny (char[] values, int startIndex, int count)
1070 throw new ArgumentNullException ();
1072 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1073 throw new ArgumentOutOfRangeException ();
1075 for (i = startIndex; i > startIndex - count; i--) {
1076 for (int j = 0; j < strlen (values); j++) {
1077 if (this.c_str[i] == values[j])
1085 public string PadLeft (int totalWidth)
1087 return PadLeft (totalWidth, ' ');
1090 public string PadLeft (int totalWidth, char padChar)
1096 throw new ArgumentException ();
1098 str = new char [totalWidth > this.length ? totalWidth : this.length];
1099 for (i = 0; i < totalWidth - this.length; i++)
1102 for (j = 0; j < this.length; i++, j++)
1103 str[i] = this.c_str[j];
1105 return new String (str);
1108 public string PadRight (int totalWidth)
1110 return PadRight (totalWidth, ' ');
1113 public string PadRight (int totalWidth, char padChar)
1119 throw new ArgumentException ();
1121 str = new char [totalWidth > this.length ? totalWidth : this.length];
1122 for (i = 0; i < this.length; i++)
1123 str[i] = this.c_str[i];
1125 for ( ; i < str.Length; i++)
1128 return new String (str);
1131 public string Remove (int startIndex, int count)
1136 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1137 throw new ArgumentOutOfRangeException ();
1139 len = this.length - count;
1141 return String.Empty;
1143 String res = new String (len);
1145 for (i = 0; i < startIndex; i++)
1146 str[i] = this.c_str[i];
1147 for (j = i + count; j < this.length; j++)
1148 str[i++] = this.c_str[j];
1153 public string Replace (char oldChar, char newChar)
1158 String res = new String (length);
1160 for (i = 0; i < this.length; i++) {
1161 if (this.c_str[i] == oldChar)
1164 str[i] = this.c_str[i];
1170 public string Replace (string oldValue, string newValue)
1172 // LAMESPEC: msdn doesn't specify what to do if either args is null
1173 int index, len, i, j;
1176 if (oldValue == null || newValue == null)
1177 throw new ArgumentNullException ();
1179 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1180 index = IndexOf (oldValue, 0);
1182 // This is the easy one ;-)
1183 return Substring (0, this.length);
1186 len = this.length - oldValue.length + newValue.length;
1188 return String.Empty;
1190 String res = new String (len);
1192 for (i = 0; i < index; i++)
1193 str[i] = this.c_str[i];
1194 for (j = 0; j < newValue.length; j++)
1195 str[i++] = newValue[j];
1196 for (j = index + oldValue.length; j < this.length; j++)
1197 str[i++] = this.c_str[j];
1202 private int splitme (char[] separators, int startIndex)
1204 /* this is basically a customized IndexOfAny() for the Split() methods */
1205 for (int i = startIndex; i < this.length; i++) {
1206 if (separators != null) {
1207 foreach (char sep in separators) {
1208 if (this.c_str[i] == sep)
1209 return i - startIndex;
1211 } else if (is_lwsp (this.c_str[i])) {
1212 return i - startIndex;
1219 public string[] Split (params char[] separator)
1223 * @separator: delimiting chars or null to split on whtspc
1225 * Returns: 1. An array consisting of a single
1226 * element (@this) if none of the delimiting
1227 * chars appear in @this. 2. An array of
1228 * substrings which are delimited by one of
1229 * the separator chars. 3. An array of
1230 * substrings separated by whitespace if
1231 * @separator is null. The Empty string should
1232 * be returned wherever 2 delimiting chars are
1235 // FIXME: would using a Queue be better?
1240 list = new ArrayList ();
1241 for (index = 0, len = 0; index < this.length; index += len + 1) {
1242 len = splitme (separator, index);
1243 len = len > -1 ? len : this.length - index;
1245 list.Add (String.Empty);
1250 str = new char [len];
1251 for (i = 0; i < len; i++)
1252 str[i] = this.c_str[index + i];
1254 list.Add (new String (str));
1258 strings = new string [list.Count];
1259 if (list.Count == 1) {
1260 /* special case for an array holding @this */
1263 for (index = 0; index < list.Count; index++)
1264 strings[index] = (string) list[index];
1270 public string[] Split (char[] separator, int maxCount)
1272 // FIXME: what to do if maxCount <= 0?
1273 // FIXME: would using Queue be better than ArrayList?
1276 int index, len, used;
1279 list = new ArrayList ();
1280 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1281 len = splitme (separator, index);
1282 len = len > -1 ? len : this.length - index;
1284 list.Add (String.Empty);
1289 str = new char [len];
1290 for (i = 0; i < len; i++)
1291 str[i] = this.c_str[index + i];
1293 list.Add (new String (str));
1298 /* fit the remaining chunk of the @this into it's own element */
1299 if (index < this.length - 1) {
1303 str = new char [this.length - index];
1304 for (i = index; i < this.length; i++)
1305 str[i - index] = this.c_str[i];
1307 list.Add (new String (str));
1310 strings = new string [list.Count];
1311 if (list.Count == 1) {
1312 /* special case for an array holding @this */
1315 for (index = 0; index < list.Count; index++)
1316 strings[index] = (string) list[index];
1322 public bool StartsWith (string value)
1324 bool startswith = true;
1328 throw new ArgumentNullException ();
1330 if (value.length > this.length)
1333 for (i = 0; i < value.length && startswith; i++)
1334 startswith = startswith && value.c_str[i] == this.c_str[i];
1339 public string Substring (int startIndex)
1344 if (startIndex < 0 || startIndex > this.length)
1345 throw new ArgumentOutOfRangeException ();
1347 len = this.length - startIndex;
1349 return String.Empty;
1350 String res = new String (len);
1352 for (i = startIndex; i < this.length; i++)
1353 str[i - startIndex] = this.c_str[i];
1358 public string Substring (int startIndex, int length)
1363 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1364 throw new ArgumentOutOfRangeException ();
1367 return String.Empty;
1369 String res = new String (length);
1371 for (i = startIndex; i < startIndex + length; i++)
1372 str[i - startIndex] = this.c_str[i];
1377 bool IConvertible.ToBoolean (IFormatProvider provider)
1379 return Convert.ToBoolean (this);
1382 byte IConvertible.ToByte (IFormatProvider provider)
1384 return Convert.ToByte (this);
1387 char IConvertible.ToChar (IFormatProvider provider)
1389 return Convert.ToChar (this);
1392 public char[] ToCharArray ()
1394 return ToCharArray (0, this.length);
1397 public char[] ToCharArray (int startIndex, int length)
1402 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1403 throw new ArgumentOutOfRangeException ();
1405 chars = new char [length];
1406 for (i = startIndex; i < length; i++)
1407 chars[i - startIndex] = this.c_str[i];
1412 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1414 return Convert.ToDateTime (this);
1417 decimal IConvertible.ToDecimal (IFormatProvider provider)
1419 return Convert.ToDecimal (this);
1422 double IConvertible.ToDouble (IFormatProvider provider)
1424 return Convert.ToDouble (this);
1427 short IConvertible.ToInt16 (IFormatProvider provider)
1429 return Convert.ToInt16 (this);
1432 int IConvertible.ToInt32 (IFormatProvider provider)
1434 return Convert.ToInt32 (this);
1437 long IConvertible.ToInt64 (IFormatProvider provider)
1439 return Convert.ToInt64 (this);
1442 public string ToLower ()
1447 String res = new String (length);
1449 for (i = 0; i < this.length; i++)
1450 str[i] = Char.ToLower (this.c_str[i]);
1456 public string ToLower (CultureInfo culture)
1458 // FIXME: implement me
1459 throw new NotImplementedException ();
1463 [CLSCompliant(false)]
1464 sbyte IConvertible.ToSByte (IFormatProvider provider)
1466 return Convert.ToSByte (this);
1469 float IConvertible.ToSingle (IFormatProvider provider)
1471 return Convert.ToSingle (this);
1474 public override string ToString ()
1479 string IConvertible.ToString (IFormatProvider format)
1484 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1486 return Convert.ToType (this, conversionType, provider);
1489 [CLSCompliant(false)]
1490 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1492 return Convert.ToUInt16 (this);
1495 [CLSCompliant(false)]
1496 uint IConvertible.ToUInt32 (IFormatProvider provider)
1498 return Convert.ToUInt32 (this);
1501 [CLSCompliant(false)]
1502 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1504 return Convert.ToUInt64 (this);
1507 public string ToUpper ()
1512 String res = new String (length);
1514 for (i = 0; i < this.length; i++)
1515 str[i] = Char.ToUpper (this.c_str[i]);
1521 public string ToUpper (CultureInfo culture)
1523 // FIXME: implement me
1524 throw new NotImplementedException ();
1527 public string Trim ()
1532 public string Trim (params char[] trimChars)
1535 bool matches = false;
1537 for (begin = 0; begin < this.length; begin++) {
1538 if (trimChars != null) {
1540 foreach (char c in trimChars) {
1541 matches = this.c_str[begin] == c;
1548 matches = is_lwsp (this.c_str[begin]);
1555 for (end = this.length - 1; end > begin; end--) {
1556 if (trimChars != null) {
1558 foreach (char c in trimChars) {
1559 matches = this.c_str[end] == c;
1566 matches = is_lwsp (this.c_str[end]);
1575 return String.Empty;
1577 return Substring (begin, end - begin);
1580 public string TrimEnd (params char[] trimChars)
1582 bool matches = true;
1585 for (end = this.length - 1; matches && end > 0; end--) {
1587 if (trimChars != null) {
1589 foreach (char c in trimChars) {
1590 matches = this.c_str[end] == c;
1595 matches = is_lwsp (this.c_str[end]);
1599 return Substring (0, end+1);
1603 return String.Empty;
1605 return Substring (0, end);
1608 public string TrimStart (params char[] trimChars)
1610 bool matches = true;
1613 for (begin = 0; matches && begin < this.length; begin++) {
1614 if (trimChars != null) {
1616 foreach (char c in trimChars) {
1617 matches = this.c_str[begin] == c;
1622 matches = is_lwsp (this.c_str[begin]);
1626 return Substring (begin, this.length - begin);
1629 return String.Empty;
1633 public static bool operator ==(string a, string b)
1635 if ((object)a == null) {
1636 if ((object)b == null)
1640 if ((object)b == null)
1643 if (a.length != b.length)
1647 for (int i = 0; i < l; i++)
1648 if (a.c_str[i] != b.c_str[i])
1654 public static bool operator !=(string a, string b)
1661 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1662 // parses format specifier of form:
1668 // N = argument number (non-negative integer)
1670 n = ParseDecimal (str, ref ptr);
1672 throw new FormatException ("Input string was not in correct format.");
1674 // M = width (non-negative integer)
1676 if (str[ptr] == ',') {
1677 left_align = (str[++ ptr] == '-');
1681 width = ParseDecimal (str, ref ptr);
1683 throw new FormatException ("Input string was not in correct format.");
1690 // F = argument format (string)
1692 if (str[ptr] == ':') {
1694 while (str[ptr] != '}')
1697 format = str.Substring (start, ptr - start);
1702 if (str[ptr ++] != '}')
1703 throw new FormatException ("Input string was not in correct format.");
1705 catch (IndexOutOfRangeException) {
1706 throw new FormatException ("Input string was not in correct format.");
1710 private static int ParseDecimal (string str, ref int ptr) {
1715 if (c < '0' || '9' < c)
1718 n = n * 10 + c - '0';