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")]
35 public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
36 public static readonly string Empty = "";
42 internal String (int storage)
45 throw new ArgumentOutOfRangeException ();
47 c_str = new char [storage];
51 unsafe public String (char *value)
55 // FIXME: can I do value.Length here?
59 for (i = 0; *(value + i) != '\0'; i++);
63 this.c_str = new char [this.length + 1];
64 for (i = 0; i < this.length; i++)
65 this.c_str[i] = *(value + i);
68 public String (char[] value)
72 // FIXME: value.Length includes the terminating null char?
73 this.length = value != null ? strlen (value): 0;
74 this.c_str = new char [this.length + 1];
75 for (i = 0; i < this.length; i++)
76 this.c_str[i] = value[i];
80 unsafe public String (sbyte *value)
82 // FIXME: consider unicode?
85 // FIXME: can I do value.Length here? */
89 for (i = 0; *(value + i) != '\0'; i++);
93 this.c_str = new char [this.length + 1];
94 for (i = 0; i < this.length; i++)
95 this.c_str[i] = (char) *(value + i);
98 public String (char c, int count)
103 this.c_str = new char [count + 1];
104 for (i = 0; i < count; i++)
108 [CLSCompliant(false)]
109 unsafe public String (char *value, int startIndex, int length)
113 if (value == null && startIndex != 0 && length != 0)
114 throw new ArgumentNullException ();
116 if (startIndex < 0 || length < 0)
117 throw new ArgumentOutOfRangeException ();
119 this.length = length;
120 this.c_str = new char [length + 1];
121 for (i = 0; i < length; i++)
122 this.c_str[i] = *(value + startIndex + i);
125 public String (char[] value, int startIndex, int length)
129 if (value == null && startIndex != 0 && length != 0)
130 throw new ArgumentNullException ();
132 if (startIndex < 0 || length < 0)
133 throw new ArgumentOutOfRangeException ();
135 this.length = length;
136 this.c_str = new char [length + 1];
137 for (i = 0; i < length; i++)
138 this.c_str[i] = value[startIndex + i];
141 [CLSCompliant(false)]
142 unsafe public String (sbyte *value, int startIndex, int length)
144 // FIXME: consider unicode?
147 if (value == null && startIndex != 0 && length != 0)
148 throw new ArgumentNullException ();
150 if (startIndex < 0 || length < 0)
151 throw new ArgumentOutOfRangeException ();
153 this.length = length;
154 this.c_str = new char [length + 1];
155 for (i = 0; i < length; i++)
156 this.c_str[i] = (char) *(value + startIndex + i);
159 [CLSCompliant(false)][MonoTODO]
160 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
162 // FIXME: implement me
167 // FIXME: is there anything we need to do here?
168 /*base.Finalize ();*/
178 [IndexerName("Chars")]
179 public char this [int index] {
181 if (index >= this.length)
182 throw new ArgumentOutOfRangeException ();
184 return this.c_str[index];
188 // Private helper methods
189 private static int strlen (char[] str)
191 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
196 private static char tolowerordinal (char c)
198 // FIXME: implement me
202 private static bool is_lwsp (char c)
204 /* this comes from the msdn docs for String.Trim() */
205 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
206 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
212 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
214 /* (hopefully) Unicode-safe Boyer-Moore implementation */
215 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
216 int h, n, he, ne, hc, nc, i;
218 if (haystack == null || needle == null)
219 throw new ArgumentNullException ();
221 /* if the search buffer is shorter than the pattern buffer, we can't match */
222 if (count < needle.length)
225 /* return an instant match if the pattern is 0-length */
226 if (needle.length == 0)
229 /* set a pointer at the end of each string */
230 ne = needle.length - 1; /* position of char before '\0' */
231 he = startIndex + count; /* position of last valid char */
233 /* init the skip table with the pattern length */
235 for (i = 0; i < 65536; i++)
238 /* set the skip value for the chars that *do* appear in the
239 * pattern buffer (needle) to the distance from the index to
240 * the end of the pattern buffer. */
241 for (nc = 0; nc < ne; nc++)
242 skiptable[(int) needle[nc]] = ne - nc;
245 while (count >= needle.length) {
246 hc = h + needle.length - 1; /* set the haystack compare pointer */
247 nc = ne; /* set the needle compare pointer */
249 /* work our way backwards until they don't match */
250 for (i = 0; nc > 0; nc--, hc--, i++)
251 if (needle[nc] != haystack[hc])
254 if (needle[nc] != haystack[hc]) {
255 n = skiptable[(int) haystack[hc]] - i;
267 public object Clone ()
269 // FIXME: implement me
273 const int StringCompareModeDirect = 0;
274 const int StringCompareModeCaseInsensitive = 1;
275 const int StringCompareModeOrdinal = 2;
277 internal static int _CompareGetLength (string strA, string strB)
279 if ((strA == null) || (strB == null))
282 return Math.Max (strA.Length, strB.Length);
285 internal static int _CompareChar (char chrA, char chrB, CultureInfo culture,
291 case StringCompareModeDirect:
292 // FIXME: We should do a culture based comparision here,
293 // but for the moment let's do it by hand.
294 // In the microsoft runtime, uppercase letters
295 // sort after lowercase letters in the default
297 if (Char.IsUpper (chrA) && Char.IsLower (chrB))
299 else if (Char.IsLower (chrA) && Char.IsUpper (chrB))
301 result = (int) (chrA - chrB);
303 case StringCompareModeCaseInsensitive:
304 result = (int) (Char.ToLower (chrA) - Char.ToLower (chrB));
306 case StringCompareModeOrdinal:
307 result = (int) (tolowerordinal (chrA) - tolowerordinal (chrB));
320 internal static int _Compare (string strA, int indexA, string strB, int indexB,
321 int length, CultureInfo culture,
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)
399 mode = ignoreCase ? StringCompareModeCaseInsensitive :
400 StringCompareModeDirect;
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 StringCompareModeOrdinal);
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 ();
822 return BoyerMoore (this.c_str, value, startIndex, count);
825 for (i = startIndex; i - startIndex + value.length <= count; ) {
826 if (this.c_str[i] == value.c_str [0]) {
830 for (j = 1; equal && j < value.length; j++) {
831 equal = this.c_str[i + j] == value.c_str [j];
832 if (this.c_str [i + j] == value.c_str [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 internal extern static string _Intern (string str);
905 public static string Intern (string str)
908 throw new ArgumentNullException ();
910 return _Intern (str);
913 [MethodImplAttribute(MethodImplOptions.InternalCall)]
914 internal extern static string _IsInterned (string str);
916 public static string IsInterned (string str)
919 throw new ArgumentNullException ();
921 return _IsInterned (str);
924 public static string Join (string separator, string[] value)
927 throw new ArgumentNullException ();
929 return Join (separator, value, 0, value.Length);
932 public static string Join (string separator, string[] value, int startIndex, int count)
934 // LAMESPEC: msdn doesn't specify what happens when separator is null
938 if (separator == null || value == null)
939 throw new ArgumentNullException ();
941 if (startIndex + count > value.Length)
942 throw new ArgumentOutOfRangeException ();
945 for (i = startIndex, used = 0; used < count; i++, used++) {
947 len += separator.length;
949 len += value[i].length;
952 // We have no elements to join?
956 String res = new String (len);
959 for (i = 0; i < value[startIndex].length; i++)
960 str[i] = value[startIndex][i];
963 for (j = startIndex + 1; used < count; j++, used++) {
966 for (k = 0; k < separator.length; k++)
967 str[i++] = separator.c_str[k];
968 for (k = 0; k < value[j].length; k++)
969 str[i++] = value[j].c_str[k];
975 public int LastIndexOf (char value)
981 for (; i >= 0; i--) {
982 if (this.c_str[i] == value)
989 public int LastIndexOf (string value)
992 throw new ArgumentNullException ();
993 if (value.length == 0)
995 if (this.length == 0)
998 return LastIndexOf (value, this.length - 1, this.length);
1001 public int LastIndexOf (char value, int startIndex)
1003 if (startIndex < 0 || startIndex >= this.length)
1004 throw new ArgumentOutOfRangeException ();
1006 for (int i = startIndex; i >= 0; i--) {
1007 if (this.c_str[i] == value)
1014 public int LastIndexOf (string value, int startIndex)
1016 return LastIndexOf (value, startIndex, startIndex + 1);
1019 public int LastIndexOf (char value, int startIndex, int count)
1021 if (startIndex < 0 || count < 0)
1022 throw new ArgumentOutOfRangeException ();
1024 if (startIndex >= this.length || startIndex - count + 1 < 0)
1025 throw new ArgumentOutOfRangeException ();
1027 for (int i = startIndex; i > startIndex - count; i--) {
1028 if (this.c_str[i] == value)
1035 public int LastIndexOf (string value, int startIndex, int count)
1037 // startIndex points to the end of value, ie. we're searching backwards.
1041 throw new ArgumentNullException ();
1043 if (startIndex < 0 || startIndex > this.length)
1044 throw new ArgumentOutOfRangeException ();
1046 if (count < 0 || startIndex - count + 1 < 0)
1047 throw new ArgumentOutOfRangeException ();
1049 if (value.length > startIndex)
1052 if (value == String.Empty)
1055 if (startIndex == this.length)
1058 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1059 len = value.length - 1;
1060 for (i = startIndex; i > startIndex - count; i--) {
1062 if (this.c_str[i] == value.c_str[len]) {
1066 for (j = 0; equal && j < len; j++)
1067 equal = this.c_str[i - j] == value.c_str[len - j];
1077 public int LastIndexOfAny (char[] values)
1079 return LastIndexOfAny (values, this.length - 1, this.length);
1082 public int LastIndexOfAny (char[] values, int startIndex)
1084 return LastIndexOfAny (values, startIndex, startIndex + 1);
1087 public int LastIndexOfAny (char[] values, int startIndex, int count)
1092 throw new ArgumentNullException ();
1094 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1095 throw new ArgumentOutOfRangeException ();
1097 for (i = startIndex; i > startIndex - count; i--) {
1098 for (int j = 0; j < strlen (values); j++) {
1099 if (this.c_str[i] == values[j])
1107 public string PadLeft (int totalWidth)
1109 return PadLeft (totalWidth, ' ');
1112 public string PadLeft (int totalWidth, char padChar)
1118 throw new ArgumentException ();
1120 str = new char [totalWidth > this.length ? totalWidth : this.length];
1121 for (i = 0; i < totalWidth - this.length; i++)
1124 for (j = 0; j < this.length; i++, j++)
1125 str[i] = this.c_str[j];
1127 return new String (str);
1130 public string PadRight (int totalWidth)
1132 return PadRight (totalWidth, ' ');
1135 public string PadRight (int totalWidth, char padChar)
1141 throw new ArgumentException ();
1143 str = new char [totalWidth > this.length ? totalWidth : this.length];
1144 for (i = 0; i < this.length; i++)
1145 str[i] = this.c_str[i];
1147 for ( ; i < str.Length; i++)
1150 return new String (str);
1153 public string Remove (int startIndex, int count)
1158 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1159 throw new ArgumentOutOfRangeException ();
1161 len = this.length - count;
1163 return String.Empty;
1165 String res = new String (len);
1167 for (i = 0; i < startIndex; i++)
1168 str[i] = this.c_str[i];
1169 for (j = i + count; j < this.length; j++)
1170 str[i++] = this.c_str[j];
1175 public string Replace (char oldChar, char newChar)
1180 String res = new String (length);
1182 for (i = 0; i < this.length; i++) {
1183 if (this.c_str[i] == oldChar)
1186 str[i] = this.c_str[i];
1192 public string Replace (string oldValue, string newValue)
1194 // If newValue is null, oldValue is removed.
1195 int index, len, i, j, newlen;
1199 if (oldValue == null)
1200 throw new ArgumentNullException ();
1202 thestring = Substring (0, this.length);
1205 // Runs until all occurences of oldValue have been replaced.
1207 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1208 index = thestring.IndexOf (oldValue, index);
1213 newlen = (newValue == null) ? 0 : newValue.length;
1214 len = thestring.length - oldValue.length + newlen;
1217 return String.Empty;
1219 String res = new String (len);
1221 for (i = 0; i < index; i++)
1222 str[i] = thestring.c_str[i];
1223 for (j = 0; j < newlen; j++)
1224 str[i++] = newValue[j];
1225 for (j = index + oldValue.length; j < thestring.length; j++)
1226 str[i++] = thestring.c_str[j];
1228 // Increment index, we're already done replacing until this index.
1234 private int splitme (char[] separators, int startIndex)
1236 /* this is basically a customized IndexOfAny() for the Split() methods */
1237 for (int i = startIndex; i < this.length; i++) {
1238 if (separators != null) {
1239 foreach (char sep in separators) {
1240 if (this.c_str[i] == sep)
1241 return i - startIndex;
1243 } else if (is_lwsp (this.c_str[i])) {
1244 return i - startIndex;
1251 public string[] Split (params char[] separator)
1255 * @separator: delimiting chars or null to split on whtspc
1257 * Returns: 1. An array consisting of a single
1258 * element (@this) if none of the delimiting
1259 * chars appear in @this. 2. An array of
1260 * substrings which are delimited by one of
1261 * the separator chars. 3. An array of
1262 * substrings separated by whitespace if
1263 * @separator is null. The Empty string should
1264 * be returned wherever 2 delimiting chars are
1267 // FIXME: would using a Queue be better?
1272 list = new ArrayList ();
1273 for (index = 0, len = 0; index < this.length; index += len + 1) {
1274 len = splitme (separator, index);
1275 len = len > -1 ? len : this.length - index;
1277 list.Add (String.Empty);
1282 str = new char [len];
1283 for (i = 0; i < len; i++)
1284 str[i] = this.c_str[index + i];
1286 list.Add (new String (str));
1290 strings = new string [list.Count];
1291 if (list.Count == 1) {
1292 /* special case for an array holding @this */
1295 for (index = 0; index < list.Count; index++)
1296 strings[index] = (string) list[index];
1302 public string[] Split (char[] separator, int maxCount)
1304 // FIXME: would using Queue be better than ArrayList?
1307 int index, len, used;
1310 return new string[0];
1311 else if (maxCount < 0)
1312 throw new ArgumentOutOfRangeException ();
1315 list = new ArrayList ();
1316 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1317 len = splitme (separator, index);
1318 len = len > -1 ? len : this.length - index;
1320 list.Add (String.Empty);
1325 str = new char [len];
1326 for (i = 0; i < len; i++)
1327 str[i] = this.c_str[index + i];
1329 list.Add (new String (str));
1334 /* fit the remaining chunk of the @this into it's own element */
1335 if (index <= this.length)
1340 str = new char [this.length - index];
1341 for (i = index; i < this.length; i++)
1342 str[i - index] = this.c_str[i];
1344 // maxCount cannot be zero if we reach this point and this means that
1345 // index can't be zero either.
1346 if (used == maxCount)
1347 list[used-1] += this.c_str[index-1] + new String (str);
1349 list.Add (new String (str));
1352 strings = new string [list.Count];
1353 if (list.Count == 1) {
1354 /* special case for an array holding @this */
1357 for (index = 0; index < list.Count; index++)
1358 strings[index] = (string) list[index];
1364 public bool StartsWith (string value)
1366 bool startswith = true;
1370 throw new ArgumentNullException ();
1372 if (value.length > this.length)
1375 for (i = 0; i < value.length && startswith; i++)
1376 startswith = startswith && value.c_str[i] == this.c_str[i];
1381 public string Substring (int startIndex)
1386 if (startIndex < 0 || startIndex > this.length)
1387 throw new ArgumentOutOfRangeException ();
1389 len = this.length - startIndex;
1391 return String.Empty;
1392 String res = new String (len);
1394 for (i = startIndex; i < this.length; i++)
1395 str[i - startIndex] = this.c_str[i];
1400 public string Substring (int startIndex, int length)
1405 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1406 throw new ArgumentOutOfRangeException ();
1409 return String.Empty;
1411 String res = new String (length);
1413 for (i = startIndex; i < startIndex + length; i++)
1414 str[i - startIndex] = this.c_str[i];
1419 bool IConvertible.ToBoolean (IFormatProvider provider)
1421 return Convert.ToBoolean (this);
1424 byte IConvertible.ToByte (IFormatProvider provider)
1426 return Convert.ToByte (this);
1429 char IConvertible.ToChar (IFormatProvider provider)
1431 return Convert.ToChar (this);
1434 public char[] ToCharArray ()
1436 return ToCharArray (0, this.length);
1439 public char[] ToCharArray (int startIndex, int length)
1444 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1445 throw new ArgumentOutOfRangeException ();
1447 chars = new char [length];
1448 for (i = startIndex; i < length; i++)
1449 chars[i - startIndex] = this.c_str[i];
1454 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1456 return Convert.ToDateTime (this);
1459 decimal IConvertible.ToDecimal (IFormatProvider provider)
1461 return Convert.ToDecimal (this);
1464 double IConvertible.ToDouble (IFormatProvider provider)
1466 return Convert.ToDouble (this);
1469 short IConvertible.ToInt16 (IFormatProvider provider)
1471 return Convert.ToInt16 (this);
1474 int IConvertible.ToInt32 (IFormatProvider provider)
1476 return Convert.ToInt32 (this);
1479 long IConvertible.ToInt64 (IFormatProvider provider)
1481 return Convert.ToInt64 (this);
1484 public string ToLower ()
1489 String res = new String (length);
1491 for (i = 0; i < this.length; i++)
1492 str[i] = Char.ToLower (this.c_str[i]);
1498 public string ToLower (CultureInfo culture)
1500 // FIXME: implement me
1501 throw new NotImplementedException ();
1505 [CLSCompliant(false)]
1506 sbyte IConvertible.ToSByte (IFormatProvider provider)
1508 return Convert.ToSByte (this);
1511 float IConvertible.ToSingle (IFormatProvider provider)
1513 return Convert.ToSingle (this);
1516 public override string ToString ()
1521 string IConvertible.ToString (IFormatProvider format)
1526 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1528 return Convert.ToType (this, conversionType, provider);
1531 [CLSCompliant(false)]
1532 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1534 return Convert.ToUInt16 (this);
1537 [CLSCompliant(false)]
1538 uint IConvertible.ToUInt32 (IFormatProvider provider)
1540 return Convert.ToUInt32 (this);
1543 [CLSCompliant(false)]
1544 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1546 return Convert.ToUInt64 (this);
1549 public string ToUpper ()
1554 String res = new String (length);
1556 for (i = 0; i < this.length; i++)
1557 str[i] = Char.ToUpper (this.c_str[i]);
1563 public string ToUpper (CultureInfo culture)
1565 // FIXME: implement me
1566 throw new NotImplementedException ();
1569 public string Trim ()
1574 public string Trim (params char[] trimChars)
1577 bool matches = false;
1579 for (begin = 0; begin < this.length; begin++) {
1580 if (trimChars != null) {
1582 foreach (char c in trimChars) {
1583 matches = this.c_str[begin] == c;
1590 matches = is_lwsp (this.c_str[begin]);
1597 for (end = this.length - 1; end > begin; end--) {
1598 if (trimChars != null) {
1600 foreach (char c in trimChars) {
1601 matches = this.c_str[end] == c;
1608 matches = is_lwsp (this.c_str[end]);
1617 return String.Empty;
1619 return Substring (begin, end - begin);
1622 public string TrimEnd (params char[] trimChars)
1624 bool matches = true;
1627 for (end = this.length - 1; matches && end > 0; end--) {
1629 if (trimChars != null) {
1631 foreach (char c in trimChars) {
1632 matches = this.c_str[end] == c;
1637 matches = is_lwsp (this.c_str[end]);
1641 return Substring (0, end+1);
1645 return String.Empty;
1647 return Substring (0, end);
1650 public string TrimStart (params char[] trimChars)
1652 bool matches = true;
1655 for (begin = 0; matches && begin < this.length; begin++) {
1656 if (trimChars != null) {
1658 foreach (char c in trimChars) {
1659 matches = this.c_str[begin] == c;
1664 matches = is_lwsp (this.c_str[begin]);
1668 return Substring (begin, this.length - begin);
1671 return String.Empty;
1675 public static bool operator ==(string a, string b)
1677 if ((object)a == null) {
1678 if ((object)b == null)
1682 if ((object)b == null)
1685 if (a.length != b.length)
1689 for (int i = 0; i < l; i++)
1690 if (a.c_str[i] != b.c_str[i])
1696 public static bool operator !=(string a, string b)
1703 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1704 // parses format specifier of form:
1710 // N = argument number (non-negative integer)
1712 n = ParseDecimal (str, ref ptr);
1714 throw new FormatException ("Input string was not in correct format.");
1716 // M = width (non-negative integer)
1718 if (str[ptr] == ',') {
1719 left_align = (str[++ ptr] == '-');
1723 width = ParseDecimal (str, ref ptr);
1725 throw new FormatException ("Input string was not in correct format.");
1732 // F = argument format (string)
1734 if (str[ptr] == ':') {
1736 while (str[ptr] != '}')
1739 format = str.Substring (start, ptr - start);
1744 if (str[ptr ++] != '}')
1745 throw new FormatException ("Input string was not in correct format.");
1747 catch (IndexOutOfRangeException) {
1748 throw new FormatException ("Input string was not in correct format.");
1752 private static int ParseDecimal (string str, ref int ptr) {
1757 if (c < '0' || '9' < c)
1760 n = n * 10 + c - '0';