From bcffecd7fb7a35d094d443721d980238ed663917 Mon Sep 17 00:00:00 2001 From: Ivan Shvedov Date: Fri, 1 Jun 2018 23:00:16 +0300 Subject: [PATCH 1/4] Add String.Format --- source/System/String.cs | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/source/System/String.cs b/source/System/String.cs index f3b14467..e5f02606 100644 --- a/source/System/String.cs +++ b/source/System/String.cs @@ -532,5 +532,125 @@ public static String IsInterned(String str) return str; } + /// + /// Replaces the format items in a string with the string representations of corresponding objects in a specified array. + /// + /// A composite format string + /// An object array that contains zero or more objects to format. + /// A copy of format in which the format items have been replaced by the string representation of the corresponding objects in args. + public static string Format(string format, params object[] args) + { + var index = 0; + var chr = '\0'; + var len = format.Length; + var token = Empty; + var output = Empty; + + if (format is null) + { + throw new ArgumentNullException("format can't be null"); + } + + if (args is null) + { + throw new ArgumentNullException("args can't be null"); + } + + for (int i = 0; i < len; i++) + { + chr = format[i]; + + switch (chr) + { + case '{': + { + if (format[i + 1] == '{') + { + output += chr; + i++; + } + else + { + token = Empty; + for (i++; i < len; i++) + { + chr = format[i]; + if (chr >= '0' && chr <= '9') + { + token += format[i]; + } + else if (chr == ':') + { + index = int.Parse(token); + token = Empty; + for (i++; i < len; i++) + { + chr = format[i]; + if (chr == '}') + { + if (token.Length > 0) + { + var method = args[index].GetType() + .GetMethod("ToString", new Type[] { typeof(string) }); + output += (method is null) + ? args[index].ToString() + : method.Invoke(args[index], new object[] { token }); + break; + } + else + { + throw new ArgumentException("Format error: empty format after ':' at index " + index); + } + } + else + { + token += chr; + } + } + + break; + } + else if (chr == '}') + { + if (token.Length > 0) + { + index = int.Parse(token); + output += args[index]; + } + else + { + throw new ArgumentException("Format error: empty {}"); + } + break; + } + else + { + throw new ArgumentException("Format error: wrong simbol in {}"); + } + } + } + + break; + } + case '}': + { + if (format[i + 1] == '}') + { + output += format[++i]; + } + + break; + } + default: + { + output += chr; + break; + } + } + } + + return output; + } + } } From 775bbad8ffb7391f954e6ebb9ff12d7094517051 Mon Sep 17 00:00:00 2001 From: Ivan Shvedov Date: Fri, 1 Jun 2018 23:23:06 +0300 Subject: [PATCH 2/4] Remove excess access to string.this[] --- source/System/String.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/System/String.cs b/source/System/String.cs index e5f02606..d9d42416 100644 --- a/source/System/String.cs +++ b/source/System/String.cs @@ -636,7 +636,8 @@ public static string Format(string format, params object[] args) { if (format[i + 1] == '}') { - output += format[++i]; + output += chr; + i++; } break; From 884d269ed933e1f143fdb53084a6650e5dc9d862 Mon Sep 17 00:00:00 2001 From: Ivan Shvedov Date: Tue, 5 Jun 2018 00:13:45 +0300 Subject: [PATCH 3/4] Add alignment support --- source/System/String.cs | 256 ++++++++++++++++++++++++++++++---------- 1 file changed, 194 insertions(+), 62 deletions(-) diff --git a/source/System/String.cs b/source/System/String.cs index d9d42416..7b9f2bd6 100644 --- a/source/System/String.cs +++ b/source/System/String.cs @@ -541,8 +541,10 @@ public static String IsInterned(String str) public static string Format(string format, params object[] args) { var index = 0; + var alignment = 0; var chr = '\0'; var len = format.Length; + var fmt = Empty; var token = Empty; var output = Empty; @@ -551,107 +553,237 @@ public static string Format(string format, params object[] args) throw new ArgumentNullException("format can't be null"); } - if (args is null) - { - throw new ArgumentNullException("args can't be null"); - } - - for (int i = 0; i < len; i++) + for (var i = 0; i < len; i++) { + token = Empty; chr = format[i]; - switch (chr) + if (chr == '{') { - case '{': + if (i + 1 == len) { - if (format[i + 1] == '{') + throw new ArgumentException("Format error: no closed brace, column " + i); + } + + if (format[i + 1] == '{') + { + output += chr; + i++; + continue; + } + else + { + alignment = 0; + fmt = Empty; + + for (i++; i < len; i++) + { + chr = format[i]; + + if (chr >= '0' && chr <= '9') + { + token += chr; + } + else if (chr == ',' || chr == ':' || chr == '}') + { + break; + } + else + { + throw new ArgumentException("Format error: wrong symbol at {}, column " + i); + } + } + + if (token.Length > 0) { - output += chr; - i++; + index = int.Parse(token); } else { - token = Empty; + throw new ArgumentException("Format error: empty {}, column " + i); + } + + if (chr == ',') + { + if (format[i + 1] == '-') + { + token = "-"; + i++; + } + else + { + token = Empty; + } + for (i++; i < len; i++) { chr = format[i]; + if (chr >= '0' && chr <= '9') { - token += format[i]; + token += chr; } - else if (chr == ':') + else if (chr == ':' || chr == '}') { - index = int.Parse(token); - token = Empty; - for (i++; i < len; i++) - { - chr = format[i]; - if (chr == '}') - { - if (token.Length > 0) - { - var method = args[index].GetType() - .GetMethod("ToString", new Type[] { typeof(string) }); - output += (method is null) - ? args[index].ToString() - : method.Invoke(args[index], new object[] { token }); - break; - } - else - { - throw new ArgumentException("Format error: empty format after ':' at index " + index); - } - } - else - { - token += chr; - } - } - break; } - else if (chr == '}') + else + { + throw new ArgumentException("Format error: wrong symbol at alignment, column " + i); + } + } + + if (token.Length > 0) + { + alignment = int.Parse(token); + } + else + { + throw new ArgumentException("Format error: empty alignment, column " + i); + } + } + + if (chr == ':') + { + token = Empty; + for (i++; i < len; i++) + { + chr = format[i]; + + if (chr == '}') { - if (token.Length > 0) - { - index = int.Parse(token); - output += args[index]; - } - else - { - throw new ArgumentException("Format error: empty {}"); - } break; } else { - throw new ArgumentException("Format error: wrong simbol in {}"); + token += chr; } } + + if (token.Length > 0) + { + fmt = token; + } + else + { + throw new ArgumentException("Format error: empty format after ':', column " + i); + } } + } - break; + if (chr != '}') + { + throw new ArgumentException("Format error: no closed brace, column " + i); } - case '}': + + if (fmt.Length > 0) { - if (format[i + 1] == '}') - { - output += chr; - i++; - } + var method = args[index].GetType().GetMethod("ToString", new Type[] { typeof(string) }); + token = (method is null) + ? args[index].ToString() + : method.Invoke(args[index], new object[] { token }).ToString(); + } + else + { + token = args[index].ToString(); + } - break; + if (alignment > 0) + { + output += token.PadLeft(alignment); + } + else if (alignment < 0) + { + output += token.PadRight(Math.Abs(alignment)); + } + else + { + output += token; } - default: + } + else if (chr == '}') + { + if (i + 1 == len) + { + throw new ArgumentException("Format error: no closed brace, column " + i); + } + + if (format[i + 1] == '}') { output += chr; - break; + i++; } + else + { + throw new ArgumentException("Format error: no closed brace, column " + i); + } + } + else + { + output += chr; } } return output; } + /// + /// Returns a new string that right-aligns the characters in this instance by padding them on the left with a specified Unicode character, for a specified total length. + /// + /// The number of characters in the resulting string, equal to the number of original characters plus any additional padding characters. + /// A Unicode padding character. + /// + public String PadLeft(int totalWidth, char paddingChar = ' ') + { + if (totalWidth < 0) + { + throw new ArgumentOutOfRangeException("totalWidth can't be less than 0"); + } + + if (Length >= totalWidth) + { + return this; + } + else + { + var pad = String.Empty; + var len = totalWidth - Length; + for (var i = 0; i < len; i++) + { + pad += paddingChar; + } + return pad + this; + } + } + + /// + /// Returns a new string that left-aligns the characters in this string by padding them on the right with a specified Unicode character, for a specified total length. + /// + /// The number of characters in the resulting string, equal to the number of original characters plus any additional padding characters. + /// A Unicode padding character. + /// + public String PadRight(int totalWidth, char paddingChar = ' ') + { + if (totalWidth < 0) + { + throw new ArgumentOutOfRangeException("totalWidth can't be less than 0"); + } + + if (Length >= totalWidth) + { + return this; + } + else + { + var pad = String.Empty; + var len = totalWidth - Length; + for (var i = 0; i < len; i++) + { + pad += paddingChar; + } + return this + pad; + } + } + } } From f8bfad4e9ba656d669fcaf16d5345efbc5c225de Mon Sep 17 00:00:00 2001 From: Ivan Shvedov Date: Wed, 13 Jun 2018 23:36:08 +0300 Subject: [PATCH 4/4] Improvments for PadLeft, PadRight Speed up to 5 times, less memory usage. --- source/System/String.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/source/System/String.cs b/source/System/String.cs index 7b9f2bd6..ba73afe1 100644 --- a/source/System/String.cs +++ b/source/System/String.cs @@ -746,13 +746,7 @@ public String PadLeft(int totalWidth, char paddingChar = ' ') } else { - var pad = String.Empty; - var len = totalWidth - Length; - for (var i = 0; i < len; i++) - { - pad += paddingChar; - } - return pad + this; + return new String(paddingChar, totalWidth - Length) + this; } } @@ -775,13 +769,7 @@ public String PadRight(int totalWidth, char paddingChar = ' ') } else { - var pad = String.Empty; - var len = totalWidth - Length; - for (var i = 0; i < len; i++) - { - pad += paddingChar; - } - return this + pad; + return this + new String(paddingChar, totalWidth - Length); } }