Showing the changes to Source/Libraries/GSF.Core/StringExtensions.cs (1 of 628 files). Show all
Unified · Split
| @@ -0,0 +1,1477 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // StringExtensions.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright © 2010, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may | ||
| 9 | // not use this file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://www.opensource.org/licenses/eclipse-1.0.php | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 02/23/2003 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // 01/24/2006 - J. Ritchie Carroll | ||
| 22 | // Migrated 2.0 version of source code from 1.1 source (GSF.Shared.String). | ||
| 23 | // 06/01/2006 - J. Ritchie Carroll | ||
| 24 | // Added ParseBoolean function to parse strings representing booleans that may be numeric. | ||
| 25 | // 07/07/2006 - J. Ritchie Carroll | ||
| 26 | // Added GetStringSegments function to break a string up into smaller chunks for parsing. | ||
| 27 | // and/or displaying. | ||
| 28 | // 08/02/2007 - J. Ritchie Carroll | ||
| 29 | // Added a CenterText method for centering strings in console applications or fixed width fonts. | ||
| 30 | // 08/03/2007 - Pinal C. Patel | ||
| 31 | // Modified the CenterText method to handle multiple lines. | ||
| 32 | // 08/21/2007 - Darrell Zuercher | ||
| 33 | // Edited code comments. | ||
| 34 | // 09/25/2007 - J. Ritchie Carroll | ||
| 35 | // Added TitleCase function to format a string with the first letter of each word capitalized. | ||
| 36 | // 04/16/2008 - Pinal C. Patel | ||
| 37 | // Made the keys of the string dictionary returned by ParseKeyValuePairs function case-insensitive. | ||
| 38 | // Added JoinKeyValuePairs overloads that does the exact opposite of ParseKeyValuePairs. | ||
| 39 | // 09/19/2008 - J. Ritchie Carroll | ||
| 40 | // Converted to C# extensions. | ||
| 41 | // 12/13/2008 - F. Russell Roberson | ||
| 42 | // Generalized ParseBoolean to include "Y", and "T". | ||
| 43 | // Added IndexOfRepeatedChar - Returns the index of the first character that is repeated. | ||
| 44 | // Added Reverse - Reverses the order of characters in a string. | ||
| 45 | // Added EnsureEnd - Ensures that a string ends with a specified char or string. | ||
| 46 | // Added EnsureStart - Ensures that a string begins with a specified char or string. | ||
| 47 | // Added IsNumeric - Test to see if a string only includes characters that can be interpreted as a number. | ||
| 48 | // Added TrimWithEllipsisMiddle - Adds an ellipsis in the middle of a string as it is reduced to a specified length. | ||
| 49 | // Added TrimWithEllipsisEnd - Trims a string to not exceed a fixed length and adds a ellipsis to string end. | ||
| 50 | // 02/10/2009 - J. Ritchie Carroll | ||
| 51 | // Added ConvertToType overloaded extensions. | ||
| 52 | // 02/17/2009 - Josh L. Patterson | ||
| 53 | // Edited Code Comments. | ||
| 54 | // 09/14/2009 - Stephen C. Wills | ||
| 55 | // Added new header and license agreement. | ||
| 56 | // 01/11/2010 - Galen K. Riley | ||
| 57 | // Bug fixes for unit tests: | ||
| 58 | // ConvertToType - Fix to throw ArgumentNullException instead of NullReferenceException for null value | ||
| 59 | // ConvertToType - Handling failed conversions better. Calling ConvertToType<int>("\0") returns properly | ||
| 60 | // JoinKeyValuePairs - Fix to throw ArgumentNullException instead of NullReferenceException | ||
| 61 | // ReplaceCharacters - Fix to throw ArgumentNullException instead of NullReferenceException for null replacementCharacter | ||
| 62 | // RemoveCharacters - Fix to throw ArgumentNullException instead of NullReferenceException for null characterTestFunction | ||
| 63 | // ReplaceCrLfs - Fix to throw ArgumentNullException instead of NullReferenceException for null value | ||
| 64 | // RegexDecode - Fix to throw ArgumentNullException instead of NullReferenceException for null value | ||
| 65 | // 12/03/2010 - J. Ritchie Carroll | ||
| 66 | // Modifed ParseKeyValuePairs such that it could handle nested pairs to any needed depth. | ||
| 67 | // 12/05/2010 - Pinal C. Patel | ||
| 68 | // Added an overload for ConvertToType() that takes CultureInfo as a parameter. | ||
| 69 | // 12/07/2010 - Pinal C. Patel | ||
| 70 | // Updated ConvertToType() to return the type default if passed in string is null or empty. | ||
| 71 | // 01/04/2011 - J. Ritchie Carroll | ||
| 72 | // Modified ConvertToType culture default to InvariantCulture for English style parsing defaults. | ||
| 73 | // 01/14/2011 - J. Ritchie Carroll | ||
| 74 | // Modified JoinKeyValuePairs to delineate values that contain nested key/value pair expressions | ||
| 75 | // such that the generated expression can correctly parsed. | ||
| 76 | // 03/23/2011 - J. Ritchie Carroll | ||
| 77 | // Modified ParseKeyValuePairs to optionally ignore duplicate keys (default behavior now). | ||
| 78 | // Removed overloads for ParseKeyValuePairs and JoinKeyValuePairs using optional parameters. | ||
| 79 | // 08/02/2011 - Pinal C. Patel | ||
| 80 | // Added RemoveInvalidFileNameCharacters() and ReplaceInvalidFileNameCharacters() methods. | ||
| 81 | // 10/8/2012 - Danyelle Gilliam | ||
| 82 | // Modified Header | ||
| 83 | // | ||
| 84 | //******************************************************************************************************* | ||
| 85 | // | ||
| 86 | //****************************************************************************************************** | ||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | #region [ Contributor License Agreements ] | ||
| 92 | |||
| 93 | //****************************************************************************************************** | ||
| 94 | // | ||
| 95 | // Copyright © 2011, Grid Protection Alliance. All Rights Reserved. | ||
| 96 | // | ||
| 97 | // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may | ||
| 98 | // not use this file except in compliance with the License. You may obtain a copy of the License at: | ||
| 99 | // | ||
| 100 | // http://www.opensource.org/licenses/eclipse-1.0.php | ||
| 101 | // | ||
| 102 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 103 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 104 | // License for the specific language governing permissions and limitations. | ||
| 105 | // | ||
| 106 | //****************************************************************************************************** | ||
| 107 | |||
| 108 | #endregion | ||
| 109 | |||
| 110 | using System; | ||
| 111 | using System.Collections.Generic; | ||
| 112 | using System.ComponentModel; | ||
| 113 | using System.Globalization; | ||
| 114 | using System.IO; | ||
| 115 | using System.Text; | ||
| 116 | using System.Text.RegularExpressions; | ||
| 117 | |||
| 118 | namespace GSF | ||
| 119 | { | ||
| 120 | /// <summary>Defines extension functions related to string manipulation.</summary> | ||
| 121 | public static class StringExtensions | ||
| 122 | { | ||
| 123 | /// <summary> | ||
| 124 | /// Parses a string intended to represent a boolean value. | ||
| 125 | /// </summary> | ||
| 126 | /// <param name="value">String representing a boolean value.</param> | ||
| 127 | /// <returns>Parsed boolean value.</returns> | ||
| 128 | /// <remarks> | ||
| 129 | /// This function, unlike Boolean.Parse, correctly parses a boolean value, even if the string value | ||
| 130 | /// specified is a number (e.g., 0 or -1). Boolean.Parse expects a string to be represented as | ||
| 131 | /// "True" or "False" (i.e., Boolean.TrueString or Boolean.FalseString respectively). | ||
| 132 | /// </remarks> | ||
| 133 | public static bool ParseBoolean(this string value) | ||
| 134 | { | ||
| 135 | if (string.IsNullOrEmpty(value)) | ||
| 136 | return false; | ||
| 137 | |||
| 138 | value = value.Trim(); | ||
| 139 | |||
| 140 | if (value.Length > 0) | ||
| 141 | { | ||
| 142 | // Try to parse string a number | ||
| 143 | int iresult; | ||
| 144 | |||
| 145 | if (int.TryParse(value, out iresult)) | ||
| 146 | { | ||
| 147 | return (iresult != 0); | ||
| 148 | } | ||
| 149 | else | ||
| 150 | { | ||
| 151 | // Try to parse string as a boolean | ||
| 152 | bool bresult; | ||
| 153 | |||
| 154 | if (bool.TryParse(value, out bresult)) | ||
| 155 | { | ||
| 156 | return bresult; | ||
| 157 | } | ||
| 158 | else | ||
| 159 | { | ||
| 160 | char test = value.ToUpper()[0]; | ||
| 161 | return (test == 'T' || test == 'Y' ? true : false); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | return false; | ||
| 167 | } | ||
| 168 | |||
| 169 | /// <summary> | ||
| 170 | /// Converts this string into the specified type. | ||
| 171 | /// </summary> | ||
| 172 | /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam> | ||
| 173 | /// <param name="value">Source string to convert to type.</param> | ||
| 174 | /// <returns><see cref="String"/> converted to specfied <see cref="Type"/>; default value of specified type T if conversion fails.</returns> | ||
| 175 | /// <remarks> | ||
| 176 | /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="String"/> to the specified type T, | ||
| 177 | /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same | ||
| 178 | /// <see cref="TypeConverter"/> to convert the original object to a <see cref="String"/>; see the | ||
| 179 | /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this. | ||
| 180 | /// </remarks> | ||
| 181 | public static T ConvertToType<T>(this string value) | ||
| 182 | { | ||
| 183 | return ConvertToType<T>(value, null); | ||
| 184 | } | ||
| 185 | |||
| 186 | /// <summary> | ||
| 187 | /// Converts this string into the specified type. | ||
| 188 | /// </summary> | ||
| 189 | /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam> | ||
| 190 | /// <param name="value">Source string to convert to type.</param> | ||
| 191 | /// <param name="type"><see cref="Type"/> to convert string to.</param> | ||
| 192 | /// <returns><see cref="String"/> converted to specfied <see cref="Type"/>; default value of specified type T if conversion fails.</returns> | ||
| 193 | /// <remarks> | ||
| 194 | /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="String"/> to the specified <paramref name="type"/>, | ||
| 195 | /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same | ||
| 196 | /// <see cref="TypeConverter"/> to convert the original object to a <see cref="String"/>; see the | ||
| 197 | /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this. | ||
| 198 | /// </remarks> | ||
| 199 | public static T ConvertToType<T>(this string value, Type type) | ||
| 200 | { | ||
| 201 | return ConvertToType<T>(value, type, null); | ||
| 202 | } | ||
| 203 | |||
| 204 | /// <summary> | ||
| 205 | /// Converts this string into the specified type. | ||
| 206 | /// </summary> | ||
| 207 | /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam> | ||
| 208 | /// <param name="value">Source string to convert to type.</param> | ||
| 209 | /// <param name="type"><see cref="Type"/> to convert string to.</param> | ||
| 210 | /// <param name="culture"><see cref="CultureInfo"/> to use for the conversion.</param> | ||
| 211 | /// <returns><see cref="String"/> converted to specfied <see cref="Type"/>; default value of specified type T if conversion fails.</returns> | ||
| 212 | /// <remarks> | ||
| 213 | /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="String"/> to the specified <paramref name="type"/>, | ||
| 214 | /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same | ||
| 215 | /// <see cref="TypeConverter"/> to convert the original object to a <see cref="String"/>; see the | ||
| 216 | /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this. | ||
| 217 | /// </remarks> | ||
| 218 | public static T ConvertToType<T>(this string value, Type type, CultureInfo culture) | ||
| 219 | { | ||
| 220 | // Don't proceed further if string is empty. | ||
| 221 | if (string.IsNullOrEmpty(value)) | ||
| 222 | return default(T); | ||
| 223 | |||
| 224 | // Initialize return type if not specified. | ||
| 225 | if ((object)type == null) | ||
| 226 | type = typeof(T); | ||
| 227 | |||
| 228 | // Initialize culture info if not specified. | ||
| 229 | if ((object)culture == null) | ||
| 230 | culture = CultureInfo.InvariantCulture; | ||
| 231 | |||
| 232 | try | ||
| 233 | { | ||
| 234 | if (type.IsAssignableFrom(typeof(bool))) | ||
| 235 | { | ||
| 236 | // Handle booleans as a special case to allow numeric entries as well as true/false | ||
| 237 | return (T)((object)value.ParseBoolean()); | ||
| 238 | } | ||
| 239 | else | ||
| 240 | { | ||
| 241 | if (type.IsAssignableFrom(typeof(IConvertible))) | ||
| 242 | { | ||
| 243 | // This is faster for native types than using type converter... | ||
| 244 | return (T)Convert.ChangeType(value, type, culture); | ||
| 245 | } | ||
| 246 | else | ||
| 247 | { | ||
| 248 | // Handle objects that have type converters (e.g., Enum, Color, Point, etc.) | ||
| 249 | TypeConverter converter = TypeDescriptor.GetConverter(type); | ||
| 250 | return (T)converter.ConvertFromString(null, culture, value); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | catch | ||
| 255 | { | ||
| 256 | return default(T); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /// <summary> | ||
| 261 | /// Turns source string into an array of string segements - each with a set maximum width - for parsing or displaying. | ||
| 262 | /// </summary> | ||
| 263 | /// <param name="value">Input string to break up into segements.</param> | ||
| 264 | /// <param name="segmentSize">Maximum size of returned segment.</param> | ||
| 265 | /// <returns>Array of string segments as parsed from source string.</returns> | ||
| 266 | /// <remarks>Returns a single element array with an empty string if source string is null or empty.</remarks> | ||
| 267 | public static string[] GetSegments(this string value, int segmentSize) | ||
| 268 | { | ||
| 269 | if (segmentSize <= 0) | ||
| 270 | throw new ArgumentOutOfRangeException("segmentSize", "segmentSize must be greater than zero."); | ||
| 271 | |||
| 272 | if (string.IsNullOrEmpty(value)) | ||
| 273 | return new string[] { "" }; | ||
| 274 | |||
| 275 | int totalSegments = (int)Math.Ceiling(value.Length / (double)segmentSize); | ||
| 276 | string[] segments = new string[totalSegments]; | ||
| 277 | |||
| 278 | for (int x = 0; x < segments.Length; x++) | ||
| 279 | { | ||
| 280 | if (x * segmentSize + segmentSize >= value.Length) | ||
| 281 | segments[x] = value.Substring(x * segmentSize); | ||
| 282 | else | ||
| 283 | segments[x] = value.Substring(x * segmentSize, segmentSize); | ||
| 284 | } | ||
| 285 | |||
| 286 | return segments; | ||
| 287 | } | ||
| 288 | |||
| 289 | /// <summary> | ||
| 290 | /// Combines a dictionary of key-value pairs in to a string. | ||
| 291 | /// </summary> | ||
| 292 | /// <param name="pairs">Dictionary of key-value pairs.</param> | ||
| 293 | /// <param name="parameterDelimeter">Character that delimits one key-value pair from another (eg. ';').</param> | ||
| 294 | /// <param name="keyValueDelimeter">Character that delimits a key from its value (eg. '=').</param> | ||
| 295 | /// <param name="startValueDelimeter">Optional character that marks the start of a value such that value could contain other | ||
| 296 | /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters (e.g., "{").</param> | ||
| 297 | /// <param name="endValueDelimeter">Optional character that marks the end of a value such that value could contain other | ||
| 298 | /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters (e.g., "}").</param> | ||
| 299 | /// <returns>A string of key-value pairs.</returns> | ||
| 300 | /// <remarks> | ||
| 301 | /// Values will be escaped within <paramref name="startValueDelimeter"/> and <paramref name="endValueDelimeter"/> | ||
| 302 | /// to contain nested key/value pair expressions like the following: <c>normalKVP=-1; nestedKVP={p1=true; p2=0.001}</c>, | ||
| 303 | /// when either the <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> are detected in the | ||
| 304 | /// value of the key/value pair. | ||
| 305 | /// </remarks> | ||
| 306 | public static string JoinKeyValuePairs(this Dictionary<string, string> pairs, char parameterDelimeter = ';', char keyValueDelimeter = '=', char startValueDelimeter = '{', char endValueDelimeter = '}') | ||
| 307 | { | ||
| 308 | // <pex> | ||
| 309 | if ((object)pairs == null) | ||
| 310 | throw new ArgumentNullException("pairs"); | ||
| 311 | // </pex> | ||
| 312 | |||
| 313 | char[] delimiters = { parameterDelimeter, keyValueDelimeter }; | ||
| 314 | StringBuilder result = new StringBuilder(); | ||
| 315 | string value; | ||
| 316 | |||
| 317 | foreach (string key in pairs.Keys) | ||
| 318 | { | ||
| 319 | value = pairs[key]; | ||
| 320 | |||
| 321 | if (value.IndexOfAny(delimiters) >= 0) | ||
| 322 | value = startValueDelimeter + value + endValueDelimeter; | ||
| 323 | |||
| 324 | result.AppendFormat("{0}{1}{2}{3}", key, keyValueDelimeter, value, parameterDelimeter); | ||
| 325 | } | ||
| 326 | |||
| 327 | return result.ToString(); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// <summary> | ||
| 331 | /// Parses key/value pair expressions from a string. Parameter pairs are delimited by <paramref name="keyValueDelimeter"/> | ||
| 332 | /// and multiple pairs separated by <paramref name="parameterDelimeter"/>. Supports encapsulated nested expressions. | ||
| 333 | /// </summary> | ||
| 334 | /// <param name="value">String containing key/value pair expressions to parse.</param> | ||
| 335 | /// <param name="parameterDelimeter">Character that delimits one key/value pair from another.</param> | ||
| 336 | /// <param name="keyValueDelimeter">Character that delimits key from value.</param> | ||
| 337 | /// <param name="startValueDelimeter">Optional character that marks the start of a value such that value could contain other | ||
| 338 | /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters.</param> | ||
| 339 | /// <param name="endValueDelimeter">Optional character that marks the end of a value such that value could contain other | ||
| 340 | /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters.</param> | ||
| 341 | /// <param name="ignoreDuplicateKeys">Flag determines whether duplicates are ignored. If flag is set to <c>false</c> an | ||
| 342 | /// <see cref="ArgumentException"/> will be thrown when all key parameters are not unique.</param> | ||
| 343 | /// <returns>Dictionary of key/value pairs.</returns> | ||
| 344 | /// <remarks> | ||
| 345 | /// <para> | ||
| 346 | /// Parses a string containing key/value pair expressions (e.g., "localPort=5001; transportProtocol=UDP; interface=0.0.0.0"). | ||
| 347 | /// This method treats all "keys" as case-insensitive. Nesting of key/value pair expressions is allowed by encapsulating the | ||
| 348 | /// value using the <paramref name="startValueDelimeter"/> and <paramref name="endValueDelimeter"/> values (e.g., | ||
| 349 | /// "dataChannel={Port=-1;Clients=localhost:8800}; commandChannel={Port=8900}; dataFormat=FloatingPoint;"). There must be one | ||
| 350 | /// <paramref name="endValueDelimeter"/> for each encountered <paramref name="startValueDelimeter"/> in the value or a | ||
| 351 | /// <see cref="FormatException"/> will be thrown. Multiple levels of nesting is supported. If the <paramref name="ignoreDuplicateKeys"/> | ||
| 352 | /// flag is set to <c>false</c> an <see cref="ArgumentException"/> will be thrown when all key parameters are not unique. Note | ||
| 353 | /// that keys within nested expressions are considered separate key/value pair strings and are not considered when checking | ||
| 354 | /// for duplicate keys. | ||
| 355 | /// </para> | ||
| 356 | /// </remarks> | ||
| 357 | /// <exception cref="ArgumentNullException">value is null.</exception> | ||
| 358 | /// <exception cref="ArgumentException">All delimeters must be unique -or- all keys must be unique when | ||
| 359 | /// <paramref name="ignoreDuplicateKeys"/> is set to <c>false</c>.</exception> | ||
| 360 | /// <exception cref="FormatException">Total nested key/value value pair expressions are mismatched -or- encountered | ||
| 361 | /// <paramref name="endValueDelimeter"/> before <paramref name="startValueDelimeter"/>.</exception> | ||
| 362 | public static Dictionary<string, string> ParseKeyValuePairs(this string value, char parameterDelimeter = ';', char keyValueDelimeter = '=', char startValueDelimeter = '{', char endValueDelimeter = '}', bool ignoreDuplicateKeys = true) | ||
| 363 | { | ||
| 364 | if (value == (string)null) | ||
| 365 | throw new ArgumentNullException("value"); | ||
| 366 | |||
| 367 | if (parameterDelimeter == keyValueDelimeter || | ||
| 368 | parameterDelimeter == startValueDelimeter || | ||
| 369 | parameterDelimeter == endValueDelimeter || | ||
| 370 | keyValueDelimeter == startValueDelimeter || | ||
| 371 | keyValueDelimeter == endValueDelimeter || | ||
| 372 | startValueDelimeter == endValueDelimeter) | ||
| 373 | throw new ArgumentException("All delimeters must be unique"); | ||
| 374 | |||
| 375 | Dictionary<string, string> keyValuePairs = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); | ||
| 376 | StringBuilder escapedValue = new StringBuilder(); | ||
| 377 | string escapedParameterDelimeter = parameterDelimeter.RegexEncode(); | ||
| 378 | string escapedKeyValueDelimeter = keyValueDelimeter.RegexEncode(); | ||
| 379 | string escapedStartValueDelimeter = startValueDelimeter.RegexEncode(); | ||
| 380 | string escapedEndValueDelimeter = endValueDelimeter.RegexEncode(); | ||
| 381 | string[] elements; | ||
| 382 | string key, unescapedValue; | ||
| 383 | bool valueEscaped = false; | ||
| 384 | int delimeterDepth = 0; | ||
| 385 | char character; | ||
| 386 | |||
| 387 | // Escape any parameter or key/value delimeters within tagged value sequences | ||
| 388 | // For example, the following string: | ||
| 389 | // "normalKVP=-1; nestedKVP={p1=true; p2=false}") | ||
| 390 | // would be encoded as: | ||
| 391 | // "normalKVP=-1; nestedKVP=p1\\u003dtrue\\u003b p2\\u003dfalse") | ||
| 392 | for (int x = 0; x < value.Length; x++) | ||
| 393 | { | ||
| 394 | character = value[x]; | ||
| 395 | |||
| 396 | if (character == startValueDelimeter) | ||
| 397 | { | ||
| 398 | if (!valueEscaped) | ||
| 399 | { | ||
| 400 | valueEscaped = true; | ||
| 401 | continue; // Don't add tag start delimeter to final value | ||
| 402 | } | ||
| 403 | else | ||
| 404 | { | ||
| 405 | // Handle nested delimeters | ||
| 406 | delimeterDepth++; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | if (character == endValueDelimeter) | ||
| 411 | { | ||
| 412 | if (valueEscaped) | ||
| 413 | { | ||
| 414 | if (delimeterDepth > 0) | ||
| 415 | { | ||
| 416 | // Handle nested delimeters | ||
| 417 | delimeterDepth--; | ||
| 418 | } | ||
| 419 | else | ||
| 420 | { | ||
| 421 | valueEscaped = false; | ||
| 422 | continue; // Don't add tag stop delimeter to final value | ||
| 423 | } | ||
| 424 | } | ||
| 425 | else | ||
| 426 | { | ||
| 427 | throw new FormatException(string.Format("Failed to parse key/value pairs: invalid delimeter mismatch. Encountered end value delimeter \'{0}\' before start value delimeter \'{1}\'.", endValueDelimeter, startValueDelimeter)); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | if (valueEscaped) | ||
| 432 | { | ||
| 433 | // Escape any delimeter characters inside nested key/value pair | ||
| 434 | if (character == parameterDelimeter) | ||
| 435 | escapedValue.Append(escapedParameterDelimeter); | ||
| 436 | else if (character == keyValueDelimeter) | ||
| 437 | escapedValue.Append(escapedKeyValueDelimeter); | ||
| 438 | else if (character == startValueDelimeter) | ||
| 439 | escapedValue.Append(escapedStartValueDelimeter); | ||
| 440 | else if (character == endValueDelimeter) | ||
| 441 | escapedValue.Append(escapedEndValueDelimeter); | ||
| 442 | else | ||
| 443 | escapedValue.Append(character); | ||
| 444 | } | ||
| 445 | else | ||
| 446 | { | ||
| 447 | escapedValue.Append(character); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | if (delimeterDepth != 0 || valueEscaped) | ||
| 452 | { | ||
| 453 | // If value is still escaped, tagged expression was not terminated | ||
| 454 | if (valueEscaped) | ||
| 455 | delimeterDepth = 1; | ||
| 456 | |||
| 457 | throw new FormatException(string.Format("Failed to parse key/value pairs: invalid delimeter mismatch. Encountered more {0} than {1}.", delimeterDepth > 0 ? "start value delimeters \'" + startValueDelimeter + "\'" : "end value delimeters \'" + endValueDelimeter + "\'", delimeterDepth < 0 ? "start value delimeters \'" + startValueDelimeter + "\'" : "end value delimeters \'" + endValueDelimeter + "\'")); | ||
| 458 | } | ||
| 459 | |||
| 460 | // Parse key/value pairs from escaped value | ||
| 461 | foreach (string parameter in escapedValue.ToString().Split(parameterDelimeter)) | ||
| 462 | { | ||
| 463 | // Parse out parameter's key/value elements | ||
| 464 | elements = parameter.Split(keyValueDelimeter); | ||
| 465 | |||
| 466 | if (elements.Length == 2) | ||
| 467 | { | ||
| 468 | // Get key expression | ||
| 469 | key = elements[0].ToString().Trim(); | ||
| 470 | |||
| 471 | // Get unescaped value expression | ||
| 472 | unescapedValue = elements[1].ToString().Trim(). | ||
| 473 | Replace(escapedParameterDelimeter, parameterDelimeter.ToString()). | ||
| 474 | Replace(escapedKeyValueDelimeter, keyValueDelimeter.ToString()). | ||
| 475 | Replace(escapedStartValueDelimeter, startValueDelimeter.ToString()). | ||
| 476 | Replace(escapedEndValueDelimeter, endValueDelimeter.ToString()); | ||
| 477 | |||
| 478 | // Add key/value pair to dictionary | ||
| 479 | if (ignoreDuplicateKeys) | ||
| 480 | { | ||
| 481 | // Add or replace key elements with unescaped value | ||
| 482 | keyValuePairs[key] = unescapedValue; | ||
| 483 | } | ||
| 484 | else | ||
| 485 | { | ||
| 486 | // Add key elements with unescaped value throwing an exception for encountered duplicate keys | ||
| 487 | if (keyValuePairs.ContainsKey(key)) | ||
| 488 | throw new ArgumentException(string.Format("Failed to parse key/value pairs: duplicate key encountered. Key \"{0}\" is not unique within the string: \"{1}\"", key, value)); | ||
| 489 | |||
| 490 | keyValuePairs.Add(key, unescapedValue); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | return keyValuePairs; | ||
| 496 | } | ||
| 497 | |||
| 498 | /// <summary> | ||
| 499 | /// Ensures parameter is not an empty or null string. Returns a single space if test value is empty. | ||
| 500 | /// </summary> | ||
| 501 | /// <param name="testValue">Value to test for null or empty.</param> | ||
| 502 | /// <returns>A non-empty string.</returns> | ||
| 503 | public static string NotEmpty(this string testValue) | ||
| 504 | { | ||
| 505 | return testValue.NotEmpty(" "); | ||
| 506 | } | ||
| 507 | |||
| 508 | /// <summary> | ||
| 509 | /// Ensures parameter is not an empty or null string. | ||
| 510 | /// </summary> | ||
| 511 | /// <param name="testValue">Value to test for null or empty.</param> | ||
| 512 | /// <param name="nonEmptyReturnValue">Value to return if <paramref name="testValue">testValue</paramref> is null or empty.</param> | ||
| 513 | /// <returns>A non-empty string.</returns> | ||
| 514 | public static string NotEmpty(this string testValue, string nonEmptyReturnValue) | ||
| 515 | { | ||
| 516 | if (string.IsNullOrEmpty(nonEmptyReturnValue)) | ||
| 517 | throw new ArgumentException("nonEmptyReturnValue cannot be null or empty"); | ||
| 518 | |||
| 519 | if (string.IsNullOrEmpty(testValue)) | ||
| 520 | return nonEmptyReturnValue; | ||
| 521 | else | ||
| 522 | return testValue; | ||
| 523 | } | ||
| 524 | |||
| 525 | /// <summary> | ||
| 526 | /// Replaces all characters passing delegate test with specified replacement character. | ||
| 527 | /// </summary> | ||
| 528 | /// <param name="value">Input string.</param> | ||
| 529 | /// <param name="replacementCharacter">Character used to replace characters passing delegate test.</param> | ||
| 530 | /// <param name="characterTestFunction">Delegate used to determine whether or not character should be replaced.</param> | ||
| 531 | /// <returns>Returns <paramref name="value" /> with all characters passing delegate test replaced.</returns> | ||
| 532 | /// <remarks>Allows you to specify a replacement character (e.g., you may want to use a non-breaking space: Convert.ToChar(160)).</remarks> | ||
| 533 | public static string ReplaceCharacters(this string value, char replacementCharacter, Func<char, bool> characterTestFunction) | ||
| 534 | { | ||
| 535 | // <pex> | ||
| 536 | if (characterTestFunction == (Func<char, bool>)null) | ||
| 537 | throw new ArgumentNullException("characterTestFunction"); | ||
| 538 | // </pex> | ||
| 539 | if (string.IsNullOrEmpty(value)) | ||
| 540 | return ""; | ||
| 541 | |||
| 542 | StringBuilder result = new StringBuilder(); | ||
| 543 | char character; | ||
| 544 | |||
| 545 | for (int x = 0; x < value.Length; x++) | ||
| 546 | { | ||
| 547 | character = value[x]; | ||
| 548 | |||
| 549 | if (characterTestFunction(character)) | ||
| 550 | result.Append(replacementCharacter); | ||
| 551 | else | ||
| 552 | result.Append(character); | ||
| 553 | } | ||
| 554 | |||
| 555 | return result.ToString(); | ||
| 556 | } | ||
| 557 | |||
| 558 | /// <summary> | ||
| 559 | /// Removes all characters passing delegate test from a string. | ||
| 560 | /// </summary> | ||
| 561 | /// <param name="value">Input string.</param> | ||
| 562 | /// <param name="characterTestFunction">Delegate used to determine whether or not character should be removed.</param> | ||
| 563 | /// <returns>Returns <paramref name="value" /> with all characters passing delegate test removed.</returns> | ||
| 564 | public static string RemoveCharacters(this string value, Func<char, bool> characterTestFunction) | ||
| 565 | { | ||
| 566 | // <pex> | ||
| 567 | if (characterTestFunction == (Func<char, bool>)null) | ||
| 568 | throw new ArgumentNullException("characterTestFunction"); | ||
| 569 | // </pex> | ||
| 570 | if (string.IsNullOrEmpty(value)) | ||
| 571 | return ""; | ||
| 572 | |||
| 573 | StringBuilder result = new StringBuilder(); | ||
| 574 | char character; | ||
| 575 | |||
| 576 | for (int x = 0; x < value.Length; x++) | ||
| 577 | { | ||
| 578 | character = value[x]; | ||
| 579 | |||
| 580 | if (!characterTestFunction(character)) | ||
| 581 | result.Append(character); | ||
| 582 | } | ||
| 583 | |||
| 584 | return result.ToString(); | ||
| 585 | } | ||
| 586 | |||
| 587 | /// <summary> | ||
| 588 | /// Removes all white space (as defined by IsWhiteSpace) from a string. | ||
| 589 | /// </summary> | ||
| 590 | /// <param name="value">Input string.</param> | ||
| 591 | /// <returns>Returns <paramref name="value" /> with all white space removed.</returns> | ||
| 592 | public static string RemoveWhiteSpace(this string value) | ||
| 593 | { | ||
| 594 | return value.RemoveCharacters(char.IsWhiteSpace); | ||
| 595 | } | ||
| 596 | |||
| 597 | /// <summary> | ||
| 598 | /// Replaces all white space characters (as defined by IsWhiteSpace) with specified replacement character. | ||
| 599 | /// </summary> | ||
| 600 | /// <param name="value">Input string.</param> | ||
| 601 | /// <param name="replacementCharacter">Character used to "replace" white space characters.</param> | ||
| 602 | /// <returns>Returns <paramref name="value" /> with all white space characters replaced.</returns> | ||
| 603 | /// <remarks>Allows you to specify a replacement character (e.g., you may want to use a non-breaking space: Convert.ToChar(160)).</remarks> | ||
| 604 | public static string ReplaceWhiteSpace(this string value, char replacementCharacter) | ||
| 605 | { | ||
| 606 | return value.ReplaceCharacters(replacementCharacter, char.IsWhiteSpace); | ||
| 607 | } | ||
| 608 | |||
| 609 | /// <summary> | ||
| 610 | /// Removes all control characters from a string. | ||
| 611 | /// </summary> | ||
| 612 | /// <param name="value">Input string.</param> | ||
| 613 | /// <returns>Returns <paramref name="value" /> with all control characters removed.</returns> | ||
| 614 | public static string RemoveControlCharacters(this string value) | ||
| 615 | { | ||
| 616 | return value.RemoveCharacters(char.IsControl); | ||
| 617 | } | ||
| 618 | |||
| 619 | /// <summary> | ||
| 620 | /// Replaces all control characters in a string with a single space. | ||
| 621 | /// </summary> | ||
| 622 | /// <param name="value">Input string.</param> | ||
| 623 | /// <returns>Returns <paramref name="value" /> with all control characters replaced as a single space.</returns> | ||
| 624 | public static string ReplaceControlCharacters(this string value) | ||
| 625 | { | ||
| 626 | return value.ReplaceControlCharacters(' '); | ||
| 627 | } | ||
| 628 | |||
| 629 | /// <summary> | ||
| 630 | /// Replaces all control characters in a string with specified replacement character. | ||
| 631 | /// </summary> | ||
| 632 | /// <param name="value">Input string.</param> | ||
| 633 | /// <param name="replacementCharacter">Character used to "replace" control characters.</param> | ||
| 634 | /// <returns>Returns <paramref name="value" /> with all control characters replaced.</returns> | ||
| 635 | /// <remarks>Allows you to specify a replacement character (e.g., you may want to use a non-breaking space: Convert.ToChar(160)).</remarks> | ||
| 636 | public static string ReplaceControlCharacters(this string value, char replacementCharacter) | ||
| 637 | { | ||
| 638 | return value.ReplaceCharacters(replacementCharacter, char.IsControl); | ||
| 639 | } | ||
| 640 | |||
| 641 | /// <summary> | ||
| 642 | /// Removes all carriage returns and line feeds from a string. | ||
| 643 | /// </summary> | ||
| 644 | /// <param name="value">Input string.</param> | ||
| 645 | /// <returns>Returns <paramref name="value" /> with all CR and LF characters removed.</returns> | ||
| 646 | public static string RemoveCrLfs(this string value) | ||
| 647 | { | ||
| 648 | return value.RemoveCharacters(c => c == '\r' || c == '\n'); | ||
| 649 | } | ||
| 650 | |||
| 651 | /// <summary> | ||
| 652 | /// Replaces all carriage return and line feed characters (as well as CR/LF sequences) in a string with specified replacement character. | ||
| 653 | /// </summary> | ||
| 654 | /// <param name="value">Input string.</param> | ||
| 655 | /// <param name="replacementCharacter">Character used to "replace" CR and LF characters.</param> | ||
| 656 | /// <returns>Returns <paramref name="value" /> with all CR and LF characters replaced.</returns> | ||
| 657 | /// <remarks>Allows you to specify a replacement character (e.g., you may want to use a non-breaking space: Convert.ToChar(160)).</remarks> | ||
| 658 | public static string ReplaceCrLfs(this string value, char replacementCharacter) | ||
| 659 | { | ||
| 660 | // <pex> | ||
| 661 | if (value == (string)null) | ||
| 662 | throw new ArgumentNullException("value"); | ||
| 663 | // </pex> | ||
| 664 | return value.Replace(Environment.NewLine, replacementCharacter.ToString()).ReplaceCharacters(replacementCharacter, c => c == '\r' || c == '\n'); | ||
| 665 | } | ||
| 666 | |||
| 667 | /// <summary> | ||
| 668 | /// Removes duplicate character strings (adjoining replication) in a string. | ||
| 669 | /// </summary> | ||
| 670 | /// <param name="value">Input string.</param> | ||
| 671 | /// <param name="duplicatedValue">String whose duplicates are to be removed.</param> | ||
| 672 | /// <returns>Returns <paramref name="value" /> with all duplicated <paramref name="duplicatedValue" /> removed.</returns> | ||
| 673 | public static string RemoveDuplicates(this string value, string duplicatedValue) | ||
| 674 | { | ||
| 675 | if (string.IsNullOrEmpty(value)) | ||
| 676 | return ""; | ||
| 677 | if (string.IsNullOrEmpty(duplicatedValue)) | ||
| 678 | return value; | ||
| 679 | |||
| 680 | string duplicate = duplicatedValue + duplicatedValue; | ||
| 681 | |||
| 682 | while (value.IndexOf(duplicate) > -1) | ||
| 683 | { | ||
| 684 | value = value.Replace(duplicate, duplicatedValue); | ||
| 685 | } | ||
| 686 | |||
| 687 | return value; | ||
| 688 | } | ||
| 689 | |||
| 690 | /// <summary> | ||
| 691 | /// Removes the terminator ('\0') from a null terminated string. | ||
| 692 | /// </summary> | ||
| 693 | /// <param name="value">Input string.</param> | ||
| 694 | /// <returns>Returns <paramref name="value" /> with all characters to the left of the terminator.</returns> | ||
| 695 | public static string RemoveNull(this string value) | ||
| 696 | { | ||
| 697 | if (string.IsNullOrEmpty(value)) | ||
| 698 | return ""; | ||
| 699 | |||
| 700 | int nullPos = value.IndexOf('\0'); | ||
| 701 | |||
| 702 | if (nullPos > -1) | ||
| 703 | return value.Substring(0, nullPos); | ||
| 704 | else | ||
| 705 | return value; | ||
| 706 | } | ||
| 707 | |||
| 708 | /// <summary> | ||
| 709 | /// Replaces all repeating white space with a single space. | ||
| 710 | /// </summary> | ||
| 711 | /// <param name="value">Input string.</param> | ||
| 712 | /// <returns>Returns <paramref name="value" /> with all duplicate white space removed.</returns> | ||
| 713 | public static string RemoveDuplicateWhiteSpace(this string value) | ||
| 714 | { | ||
| 715 | return value.RemoveDuplicateWhiteSpace(' '); | ||
| 716 | } | ||
| 717 | |||
| 718 | /// <summary> | ||
| 719 | /// Replaces all repeating white space with specified spacing character. | ||
| 720 | /// </summary> | ||
| 721 | /// <param name="value">Input string.</param> | ||
| 722 | /// <param name="spacingCharacter">Character value to use to insert as single white space value.</param> | ||
| 723 | /// <returns>Returns <paramref name="value" /> with all duplicate white space removed.</returns> | ||
| 724 | /// <remarks>This function allows you to specify spacing character (e.g., you may want to use a non-breaking space: <c>Convert.ToChar(160)</c>).</remarks> | ||
| 725 | public static string RemoveDuplicateWhiteSpace(this string value, char spacingCharacter) | ||
| 726 | { | ||
| 727 | if (string.IsNullOrEmpty(value)) | ||
| 728 | return ""; | ||
| 729 | |||
| 730 | StringBuilder result = new StringBuilder(); | ||
| 731 | bool lastCharWasSpace = false; | ||
| 732 | char character; | ||
| 733 | |||
| 734 | for (int x = 0; x < value.Length; x++) | ||
| 735 | { | ||
| 736 | character = value[x]; | ||
| 737 | |||
| 738 | if (char.IsWhiteSpace(character)) | ||
| 739 | { | ||
| 740 | lastCharWasSpace = true; | ||
| 741 | } | ||
| 742 | else | ||
| 743 | { | ||
| 744 | if (lastCharWasSpace) | ||
| 745 | result.Append(spacingCharacter); | ||
| 746 | |||
| 747 | result.Append(character); | ||
| 748 | lastCharWasSpace = false; | ||
| 749 | } | ||
| 750 | } | ||
| 751 | |||
| 752 | return result.ToString(); | ||
| 753 | } | ||
| 754 | |||
| 755 | /// <summary> | ||
| 756 | /// Removes all invalid file name characters (\ / : * ? " < > |) from a string. | ||
| 757 | /// </summary> | ||
| 758 | /// <param name="value">Input string.</param> | ||
| 759 | /// <returns>Returns <paramref name="value" /> with all invalid file name characters removed.</returns> | ||
| 760 | public static string RemoveInvalidFileNameCharacters(this string value) | ||
| 761 | { | ||
| 762 | return value.RemoveCharacters(c => Array.IndexOf(Path.GetInvalidFileNameChars(), c) >= 0); | ||
| 763 | } | ||
| 764 | |||
| 765 | /// <summary> | ||
| 766 | /// Replaces all invalid file name characters (\ / : * ? " < > |) in a string with the specified <paramref name="replacementCharacter"/>. | ||
| 767 | /// </summary> | ||
| 768 | /// <param name="value">Input string.</param> | ||
| 769 | /// <param name="replacementCharacter">Character used to replace the invalid characters.</param> | ||
| 770 | /// <returns>>Returns <paramref name="value" /> with all invalid file name characters replaced.</returns> | ||
| 771 | public static string ReplaceInvalidFileNameCharacters(this string value, char replacementCharacter) | ||
| 772 | { | ||
| 773 | return value.ReplaceCharacters(replacementCharacter, c => Array.IndexOf(Path.GetInvalidFileNameChars(), c) >= 0); | ||
| 774 | } | ||
| 775 | |||
| 776 | /// <summary> | ||
| 777 | /// Counts the total number of the occurances of a character in the given string. | ||
| 778 | /// </summary> | ||
| 779 | /// <param name="value">Input string.</param> | ||
| 780 | /// <param name="characterToCount">Character to be counted.</param> | ||
| 781 | /// <returns>Total number of the occurances of <paramref name="characterToCount" /> in the given string.</returns> | ||
| 782 | public static int CharCount(this string value, char characterToCount) | ||
| 783 | { | ||
| 784 | if (string.IsNullOrEmpty(value)) | ||
| 785 | return 0; | ||
| 786 | |||
| 787 | int total = 0; | ||
| 788 | |||
| 789 | for (int x = 0; x < value.Length; x++) | ||
| 790 | { | ||
| 791 | if (value[x] == characterToCount) | ||
| 792 | total++; | ||
| 793 | } | ||
| 794 | |||
| 795 | return total; | ||
| 796 | } | ||
| 797 | |||
| 798 | /// <summary> | ||
| 799 | /// Tests to see if a string is contains only digits based on Char.IsDigit function. | ||
| 800 | /// </summary> | ||
| 801 | /// <param name="value">Input string.</param> | ||
| 802 | /// <returns>True, if all string's characters are digits; otherwise, false.</returns> | ||
| 803 | /// <seealso cref="char.IsDigit(char)"/> | ||
| 804 | public static bool IsAllDigits(this string value) | ||
| 805 | { | ||
| 806 | if (string.IsNullOrEmpty(value)) | ||
| 807 | return false; | ||
| 808 | |||
| 809 | value = value.Trim(); | ||
| 810 | if (value.Length == 0) | ||
| 811 | return false; | ||
| 812 | |||
| 813 | for (int x = 0; x < value.Length; x++) | ||
| 814 | { | ||
| 815 | if (!char.IsDigit(value[x])) | ||
| 816 | return false; | ||
| 817 | } | ||
| 818 | |||
| 819 | return true; | ||
| 820 | } | ||
| 821 | |||
| 822 | /// <summary> | ||
| 823 | /// Tests to see if a string contains only numbers based on Char.IsNumber function. | ||
| 824 | /// </summary> | ||
| 825 | /// <param name="value">Input string.</param> | ||
| 826 | /// <returns>True, if all string's characters are numbers; otherwise, false.</returns> | ||
| 827 | /// <seealso cref="char.IsNumber(char)"/> | ||
| 828 | public static bool IsAllNumbers(this string value) | ||
| 829 | { | ||
| 830 | if (string.IsNullOrEmpty(value)) | ||
| 831 | return false; | ||
| 832 | |||
| 833 | value = value.Trim(); | ||
| 834 | if (value.Length == 0) | ||
| 835 | return false; | ||
| 836 | |||
| 837 | for (int x = 0; x < value.Length; x++) | ||
| 838 | { | ||
| 839 | if (!char.IsNumber(value[x])) | ||
| 840 | return false; | ||
| 841 | } | ||
| 842 | |||
| 843 | return true; | ||
| 844 | } | ||
| 845 | |||
| 846 | /// <summary> | ||
| 847 | /// Tests to see if a string's letters are all upper case. | ||
| 848 | /// </summary> | ||
| 849 | /// <param name="value">Input string.</param> | ||
| 850 | /// <returns>True, if all string's letter characters are upper case; otherwise, false.</returns> | ||
| 851 | public static bool IsAllUpper(this string value) | ||
| 852 | { | ||
| 853 | if (string.IsNullOrEmpty(value)) | ||
| 854 | return false; | ||
| 855 | |||
| 856 | value = value.Trim(); | ||
| 857 | if (value.Length == 0) | ||
| 858 | return false; | ||
| 859 | |||
| 860 | for (int x = 0; x < value.Length; x++) | ||
| 861 | { | ||
| 862 | if (char.IsLetter(value[x]) && !char.IsUpper(value[x])) | ||
| 863 | return false; | ||
| 864 | } | ||
| 865 | |||
| 866 | return true; | ||
| 867 | } | ||
| 868 | |||
| 869 | /// <summary> | ||
| 870 | /// Tests to see if a string's letters are all lower case. | ||
| 871 | /// </summary> | ||
| 872 | /// <param name="value">Input string.</param> | ||
| 873 | /// <returns>True, if all string's letter characters are lower case; otherwise, false.</returns> | ||
| 874 | public static bool IsAllLower(this string value) | ||
| 875 | { | ||
| 876 | if (string.IsNullOrEmpty(value)) | ||
| 877 | return false; | ||
| 878 | |||
| 879 | value = value.Trim(); | ||
| 880 | if (value.Length == 0) | ||
| 881 | return false; | ||
| 882 | |||
| 883 | for (int x = 0; x < value.Length; x++) | ||
| 884 | { | ||
| 885 | if (char.IsLetter(value[x]) && !char.IsLower(value[x])) | ||
| 886 | return false; | ||
| 887 | } | ||
| 888 | |||
| 889 | return true; | ||
| 890 | } | ||
| 891 | |||
| 892 | /// <summary> | ||
| 893 | /// Tests to see if a string contains only letters. | ||
| 894 | /// </summary> | ||
| 895 | /// <param name="value">Input string.</param> | ||
| 896 | /// <returns>True, if all string's characters are letters; otherwise, false.</returns> | ||
| 897 | /// <remarks>Any non-letter character (e.g., punctuation marks) causes this function to return false (See overload to ignore punctuation | ||
| 898 | /// marks.).</remarks> | ||
| 899 | public static bool IsAllLetters(this string value) | ||
| 900 | { | ||
| 901 | return value.IsAllLetters(false); | ||
| 902 | } | ||
| 903 | |||
| 904 | /// <summary> | ||
| 905 | /// Tests to see if a string contains only letters. | ||
| 906 | /// </summary> | ||
| 907 | /// <param name="value">Input string.</param> | ||
| 908 | /// <param name="ignorePunctuation">Set to True to ignore punctuation.</param> | ||
| 909 | /// <returns>True, if all string's characters are letters; otherwise, false.</returns> | ||
| 910 | public static bool IsAllLetters(this string value, bool ignorePunctuation) | ||
| 911 | { | ||
| 912 | if (string.IsNullOrEmpty(value)) | ||
| 913 | return false; | ||
| 914 | |||
| 915 | value = value.Trim(); | ||
| 916 | if (value.Length == 0) | ||
| 917 | return false; | ||
| 918 | |||
| 919 | for (int x = 0; x < value.Length; x++) | ||
| 920 | { | ||
| 921 | if (ignorePunctuation) | ||
| 922 | { | ||
| 923 | if (!(char.IsLetter(value[x]) || char.IsPunctuation(value[x]))) | ||
| 924 | return false; | ||
| 925 | } | ||
| 926 | else | ||
| 927 | { | ||
| 928 | if (!char.IsLetter(value[x])) | ||
| 929 | return false; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | return true; | ||
| 934 | } | ||
| 935 | |||
| 936 | /// <summary> | ||
| 937 | /// Tests to see if a string contains only letters or digits. | ||
| 938 | /// </summary> | ||
| 939 | /// <param name="value">Input string.</param> | ||
| 940 | /// <returns>True, if all string's characters are either letters or digits; otherwise, false.</returns> | ||
| 941 | /// <remarks>Any non-letter, non-digit character (e.g., punctuation marks) causes this function to return false (See overload to ignore | ||
| 942 | /// punctuation marks.).</remarks> | ||
| 943 | public static bool IsAllLettersOrDigits(this string value) | ||
| 944 | { | ||
| 945 | return value.IsAllLettersOrDigits(false); | ||
| 946 | } | ||
| 947 | |||
| 948 | /// <summary> | ||
| 949 | /// Tests to see if a string contains only letters or digits. | ||
| 950 | /// </summary> | ||
| 951 | /// <param name="value">Input string.</param> | ||
| 952 | /// <param name="ignorePunctuation">Set to True to ignore punctuation.</param> | ||
| 953 | /// <returns>True, if all string's characters are letters or digits; otherwise, false.</returns> | ||
| 954 | public static bool IsAllLettersOrDigits(this string value, bool ignorePunctuation) | ||
| 955 | { | ||
| 956 | if (string.IsNullOrEmpty(value)) | ||
| 957 | return false; | ||
| 958 | |||
| 959 | value = value.Trim(); | ||
| 960 | if (value.Length == 0) | ||
| 961 | return false; | ||
| 962 | |||
| 963 | for (int x = 0; x < value.Length; x++) | ||
| 964 | { | ||
| 965 | if (ignorePunctuation) | ||
| 966 | { | ||
| 967 | if (!(char.IsLetterOrDigit(value[x]) || char.IsPunctuation(value[x]))) | ||
| 968 | return false; | ||
| 969 | } | ||
| 970 | else | ||
| 971 | { | ||
| 972 | if (!char.IsLetterOrDigit(value[x])) | ||
| 973 | return false; | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | return true; | ||
| 978 | } | ||
| 979 | |||
| 980 | /// <summary> | ||
| 981 | /// Decodes the specified Regular Expression character back into a standard Unicode character. | ||
| 982 | /// </summary> | ||
| 983 | /// <param name="value">Regular Expression character to decode back into a Unicode character.</param> | ||
| 984 | /// <returns>Standard Unicode character representation of specified Regular Expression character.</returns> | ||
| 985 | public static char RegexDecode(this string value) | ||
| 986 | { | ||
| 987 | // <pex> | ||
| 988 | if (value == (string)null) | ||
| 989 | throw new ArgumentNullException("value"); | ||
| 990 | // </pex> | ||
| 991 | return Convert.ToChar(Convert.ToUInt16(value.Replace("\\u", "0x"), 16)); | ||
| 992 | } | ||
| 993 | |||
| 994 | /// <summary> | ||
| 995 | /// Encodes a string into a base-64 string. | ||
| 996 | /// </summary> | ||
| 997 | /// <param name="value">Input string.</param> | ||
| 998 | /// <remarks> | ||
| 999 | /// <para>Performs a base-64 style of string encoding useful for data obfuscation or safe XML data string transmission.</para> | ||
| 1000 | /// <para>Note: This function encodes a "String". Use the Convert.ToBase64String function to encode a binary data buffer.</para> | ||
| 1001 | /// </remarks> | ||
| 1002 | /// <returns>A <see cref="String"></see> value representing the encoded input of <paramref name="value"/>.</returns> | ||
| 1003 | public static string Base64Encode(this string value) | ||
| 1004 | { | ||
| 1005 | return Convert.ToBase64String(Encoding.Unicode.GetBytes(value)); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | /// <summary> | ||
| 1009 | /// Decodes a given base-64 encoded string encoded with <see cref="Base64Encode" />. | ||
| 1010 | /// </summary> | ||
| 1011 | /// <param name="value">Input string.</param> | ||
| 1012 | /// <remarks>Note: This function decodes value back into a "String". Use the Convert.FromBase64String function to decode a base-64 encoded | ||
| 1013 | /// string back into a binary data buffer.</remarks> | ||
| 1014 | /// <returns>A <see cref="String"></see> value representing the decoded input of <paramref name="value"/>.</returns> | ||
| 1015 | public static string Base64Decode(this string value) | ||
| 1016 | { | ||
| 1017 | return Encoding.Unicode.GetString(Convert.FromBase64String(value)); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | /// <summary> | ||
| 1021 | /// Converts the provided string into title case (upper case first letter of each word). | ||
| 1022 | /// </summary> | ||
| 1023 | /// <param name="value">Input string.</param> | ||
| 1024 | /// <remarks>Note: This function performs "ToLower" in input string then applies TextInfo.ToTitleCase for CurrentCulture. This way, even | ||
| 1025 | /// strings formatted in all-caps will still be properly formatted.</remarks> | ||
| 1026 | /// <returns>A <see cref="String"/> that has the first letter of each word capitalized.</returns> | ||
| 1027 | public static string ToTitleCase(this string value) | ||
| 1028 | { | ||
| 1029 | if (string.IsNullOrEmpty(value)) | ||
| 1030 | return ""; | ||
| 1031 | |||
| 1032 | return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value.ToLower()); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | /// <summary> | ||
| 1036 | /// Truncates the provided string from the left if it is longer that specified length. | ||
| 1037 | /// </summary> | ||
| 1038 | /// <param name="value">A <see cref="String"/> value that is to be truncated.</param> | ||
| 1039 | /// <param name="maxLength">The maximum number of characters that <paramref name="value"/> can be.</param> | ||
| 1040 | /// <returns>A <see cref="String"/> that is the truncated version of the <paramref name="value"/> string.</returns> | ||
| 1041 | public static string TruncateLeft(this string value, int maxLength) | ||
| 1042 | { | ||
| 1043 | if (string.IsNullOrEmpty(value)) | ||
| 1044 | return ""; | ||
| 1045 | |||
| 1046 | if (value.Length > maxLength) | ||
| 1047 | return value.Substring(value.Length - maxLength); | ||
| 1048 | |||
| 1049 | return value; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | /// <summary> | ||
| 1053 | /// Truncates the provided string from the right if it is longer that specified length. | ||
| 1054 | /// </summary> | ||
| 1055 | /// <param name="value">A <see cref="String"/> value that is to be truncated.</param> | ||
| 1056 | /// <param name="maxLength">The maximum number of characters that <paramref name="value"/> can be.</param> | ||
| 1057 | /// <returns>A <see cref="String"/> that is the truncated version of the <paramref name="value"/> string.</returns> | ||
| 1058 | public static string TruncateRight(this string value, int maxLength) | ||
| 1059 | { | ||
| 1060 | if (string.IsNullOrEmpty(value)) | ||
| 1061 | return ""; | ||
| 1062 | |||
| 1063 | if (value.Length > maxLength) | ||
| 1064 | return value.Substring(0, maxLength); | ||
| 1065 | |||
| 1066 | return value; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /// <summary> | ||
| 1070 | /// Centers text within the specified maximum length, biased to the left. | ||
| 1071 | /// Text will be padded to the left and right with spaces. | ||
| 1072 | /// If value is greater than specified maximum length, value returned will be truncated from the right. | ||
| 1073 | /// </summary> | ||
| 1074 | /// <remarks> | ||
| 1075 | /// Handles multiple lines of text separated by Environment.NewLine. | ||
| 1076 | /// </remarks> | ||
| 1077 | /// <param name="value">A <see cref="String"/> to be centered.</param> | ||
| 1078 | /// <param name="maxLength">An <see cref="Int32"/> that is the maximum length of padding.</param> | ||
| 1079 | /// <returns>The centered string value.</returns> | ||
| 1080 | public static string CenterText(this string value, int maxLength) | ||
| 1081 | { | ||
| 1082 | return value.CenterText(maxLength, ' '); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | /// <summary> | ||
| 1086 | /// Centers text within the specified maximum length, biased to the left. | ||
| 1087 | /// Text will be padded to the left and right with specified padding character. | ||
| 1088 | /// If value is greater than specified maximum length, value returned will be truncated from the right. | ||
| 1089 | /// </summary> | ||
| 1090 | /// <remarks> | ||
| 1091 | /// Handles multiple lines of text separated by <c>Environment.NewLine</c>. | ||
| 1092 | /// </remarks> | ||
| 1093 | /// <param name="value">A <see cref="String"/> to be centered.</param> | ||
| 1094 | /// <param name="maxLength">An <see cref="Int32"/> that is the maximum length of padding.</param> | ||
| 1095 | /// <param name="paddingCharacter">The <see cref="char"/> value to pad with.</param> | ||
| 1096 | /// <returns>The centered string value.</returns> | ||
| 1097 | public static string CenterText(this string value, int maxLength, char paddingCharacter) | ||
| 1098 | { | ||
| 1099 | if ((object)value == null) | ||
| 1100 | value = ""; | ||
| 1101 | |||
| 1102 | // If the text to be centered contains multiple lines, centers all the lines individually. | ||
| 1103 | StringBuilder result = new StringBuilder(); | ||
| 1104 | string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); | ||
| 1105 | string line; | ||
| 1106 | int lastLineIndex = lines.Length - 1; //(lines.Length != 0 && lines[lines.Length - 1].Trim() == string.Empty ? lines.Length - 2 : lines.Length - 1); | ||
| 1107 | |||
| 1108 | for (int i = 0; i <= lastLineIndex; i++) | ||
| 1109 | { | ||
| 1110 | // Gets current line. | ||
| 1111 | line = lines[i]; | ||
| 1112 | |||
| 1113 | // Skips the last empty line as a result of split if original text had multiple lines. | ||
| 1114 | if (i == lastLineIndex && line.Trim().Length == 0) | ||
| 1115 | continue; | ||
| 1116 | |||
| 1117 | if (line.Length >= maxLength) | ||
| 1118 | { | ||
| 1119 | // Truncates excess characters on the right. | ||
| 1120 | result.Append(line.Substring(0, maxLength)); | ||
| 1121 | } | ||
| 1122 | else | ||
| 1123 | { | ||
| 1124 | int remainingSpace = maxLength - line.Length; | ||
| 1125 | int leftSpaces; | ||
| 1126 | int rightSpaces; | ||
| 1127 | |||
| 1128 | // Splits remaining space between the left and the right. | ||
| 1129 | leftSpaces = remainingSpace / 2; | ||
| 1130 | rightSpaces = leftSpaces; | ||
| 1131 | |||
| 1132 | // Adds any remaining odd space to the right (bias text to the left). | ||
| 1133 | if (remainingSpace % 2 > 0) | ||
| 1134 | rightSpaces++; | ||
| 1135 | |||
| 1136 | result.Append(new string(paddingCharacter, leftSpaces)); | ||
| 1137 | result.Append(line); | ||
| 1138 | result.Append(new string(paddingCharacter, rightSpaces)); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | // Creates a new line only if the original text contains multiple lines. | ||
| 1142 | if (i < lastLineIndex) | ||
| 1143 | result.AppendLine(); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | return result.ToString(); | ||
| 1147 | } | ||
| 1148 | /// <summary> | ||
| 1149 | /// Performs a case insensitive string replacement. | ||
| 1150 | /// </summary> | ||
| 1151 | /// <param name="value">The string to examine.</param> | ||
| 1152 | /// <param name="fromText">The value to replace.</param> | ||
| 1153 | /// <param name="toText">The new value to be inserted</param> | ||
| 1154 | /// <returns>A string with replacements.</returns> | ||
| 1155 | public static string ReplaceCaseInsensitive(this string value, string fromText, string toText) | ||
| 1156 | { | ||
| 1157 | return (new Regex(fromText, RegexOptions.IgnoreCase | RegexOptions.Multiline)).Replace(value, toText); | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | /// <summary> | ||
| 1161 | /// Ensures a string starts with a specific character. | ||
| 1162 | /// </summary> | ||
| 1163 | /// <param name="value">Input string to process.</param> | ||
| 1164 | /// <param name="startChar">The character desired at string start.</param> | ||
| 1165 | /// <returns>The sent string with character at the start.</returns> | ||
| 1166 | public static string EnsureStart(this string value, char startChar) | ||
| 1167 | { | ||
| 1168 | return EnsureStart(value, startChar, false); | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | /// <summary> | ||
| 1172 | /// Ensures a string starts with a specific character. | ||
| 1173 | /// </summary> | ||
| 1174 | /// <param name="value">Input string to process.</param> | ||
| 1175 | /// <param name="startChar">The character desired at string start.</param> | ||
| 1176 | /// <param name="removeRepeatingChar">Set to <c>true</c> to ensure one and only one instance of <paramref name="startChar"/>.</param> | ||
| 1177 | /// <returns>The sent string with character at the start.</returns> | ||
| 1178 | public static string EnsureStart(this string value, char startChar, bool removeRepeatingChar) | ||
| 1179 | { | ||
| 1180 | if (string.IsNullOrEmpty(value)) | ||
| 1181 | return ""; | ||
| 1182 | |||
| 1183 | if (startChar == 0) | ||
| 1184 | return value; | ||
| 1185 | |||
| 1186 | if (value[0] == startChar) | ||
| 1187 | { | ||
| 1188 | if (removeRepeatingChar) | ||
| 1189 | return value.Substring(LastIndexOfRepeatedChar(value, startChar, 0)); | ||
| 1190 | return value; | ||
| 1191 | } | ||
| 1192 | else | ||
| 1193 | return string.Concat(startChar, value); | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | /// <summary> | ||
| 1197 | /// Ensures a string starts with a specific string. | ||
| 1198 | /// </summary> | ||
| 1199 | /// <param name="value">Input string to process.</param> | ||
| 1200 | /// <param name="startString">The string desired at string start.</param> | ||
| 1201 | /// <returns>The sent string with string at the start.</returns> | ||
| 1202 | public static string EnsureStart(this string value, string startString) | ||
| 1203 | { | ||
| 1204 | if (string.IsNullOrEmpty(value)) | ||
| 1205 | return ""; | ||
| 1206 | |||
| 1207 | if (string.IsNullOrEmpty(startString)) | ||
| 1208 | return value; | ||
| 1209 | |||
| 1210 | if (value.IndexOf(startString) == 0) | ||
| 1211 | return value; | ||
| 1212 | else | ||
| 1213 | return string.Concat(startString, value); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | /// <summary> | ||
| 1217 | /// Ensures a string ends with a specific character. | ||
| 1218 | /// </summary> | ||
| 1219 | /// <param name="value">Input string to process.</param> | ||
| 1220 | /// <param name="endChar">The character desired at string's end.</param> | ||
| 1221 | /// <returns>The sent string with character at the end.</returns> | ||
| 1222 | public static string EnsureEnd(this string value, char endChar) | ||
| 1223 | { | ||
| 1224 | return EnsureEnd(value, endChar, false); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | /// <summary> | ||
| 1228 | /// Ensures a string ends with a specific character. | ||
| 1229 | /// </summary> | ||
| 1230 | /// <param name="value">Input string to process.</param> | ||
| 1231 | /// <param name="endChar">The character desired at string's end.</param> | ||
| 1232 | /// <param name="removeRepeatingChar">Set to <c>true</c> to ensure one and only one instance of <paramref name="endChar"/>.</param> | ||
| 1233 | /// <returns>The sent string with character at the end.</returns> | ||
| 1234 | public static string EnsureEnd(this string value, char endChar, bool removeRepeatingChar) | ||
| 1235 | { | ||
| 1236 | if (string.IsNullOrEmpty(value)) | ||
| 1237 | return ""; | ||
| 1238 | |||
| 1239 | if (endChar == 0) | ||
| 1240 | return value; | ||
| 1241 | |||
| 1242 | if (value[value.Length - 1] == endChar) | ||
| 1243 | { | ||
| 1244 | if (removeRepeatingChar) | ||
| 1245 | { | ||
| 1246 | int i = LastIndexOfRepeatedChar(value.Reverse(), endChar, 0); | ||
| 1247 | return value.Substring(0, value.Length - i); | ||
| 1248 | } | ||
| 1249 | return value; | ||
| 1250 | } | ||
| 1251 | else | ||
| 1252 | return string.Concat(value, endChar); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | /// <summary> | ||
| 1256 | /// Ensures a string ends with a specific string. | ||
| 1257 | /// </summary> | ||
| 1258 | /// <param name="value">Input string to process.</param> | ||
| 1259 | /// <param name="endString">The string desired at string's end.</param> | ||
| 1260 | /// <returns>The sent string with string at the end.</returns> | ||
| 1261 | public static string EnsureEnd(this string value, string endString) | ||
| 1262 | { | ||
| 1263 | if (string.IsNullOrEmpty(value)) | ||
| 1264 | return ""; | ||
| 1265 | |||
| 1266 | if (string.IsNullOrEmpty(endString)) | ||
| 1267 | return value; | ||
| 1268 | |||
| 1269 | if (value.EndsWith(endString)) | ||
| 1270 | return value; | ||
| 1271 | else | ||
| 1272 | return string.Concat(value, endString); | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | /// <summary> | ||
| 1276 | /// Reverses the order of the characters in a string. | ||
| 1277 | /// </summary> | ||
| 1278 | /// <param name="value">Input string to process.</param> | ||
| 1279 | /// <returns>The reversed string.</returns> | ||
| 1280 | public static string Reverse(this string value) | ||
| 1281 | { | ||
| 1282 | // Experimented with several approaches. This is the fastest. | ||
| 1283 | // Replaced original code that yielded 1.5% performance increase. | ||
| 1284 | // This code is faster than Array.Reverse. | ||
| 1285 | |||
| 1286 | if (string.IsNullOrEmpty(value)) | ||
| 1287 | return ""; | ||
| 1288 | |||
| 1289 | char[] arrChar = value.ToCharArray(); | ||
| 1290 | char temp; | ||
| 1291 | int arrLength = arrChar.Length; | ||
| 1292 | int j; | ||
| 1293 | |||
| 1294 | // Works for odd and even length strings since middle char is not swapped for an odd length string | ||
| 1295 | for (int i = 0; i < arrLength / 2; i++) | ||
| 1296 | { | ||
| 1297 | j = arrLength - i - 1; | ||
| 1298 | temp = arrChar[i]; | ||
| 1299 | arrChar[i] = arrChar[j]; | ||
| 1300 | arrChar[j] = temp; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | return new string(arrChar); | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | /// <summary> | ||
| 1307 | /// Searches a string for a repeated instance of the specified <paramref name="characterToFind"/> from specified <paramref name="startIndex"/>. | ||
| 1308 | /// </summary> | ||
| 1309 | /// <param name="value">The string to process.</param> | ||
| 1310 | /// <param name="characterToFind">The character of interest.</param> | ||
| 1311 | /// <param name="startIndex">The index from which to begin the search.</param> | ||
| 1312 | /// <returns>The index of the first instance of the character that is repeated or (-1) if no repeated chars found.</returns> | ||
| 1313 | public static int IndexOfRepeatedChar(this string value, char characterToFind, int startIndex) | ||
| 1314 | { | ||
| 1315 | if (string.IsNullOrEmpty(value)) | ||
| 1316 | return -1; | ||
| 1317 | |||
| 1318 | if (startIndex < 0) | ||
| 1319 | return -1; | ||
| 1320 | |||
| 1321 | if (characterToFind == 0) | ||
| 1322 | return -1; | ||
| 1323 | |||
| 1324 | char c = (char)0; | ||
| 1325 | |||
| 1326 | for (int i = startIndex; i < value.Length; i++) | ||
| 1327 | { | ||
| 1328 | if (value[i] == characterToFind) | ||
| 1329 | { | ||
| 1330 | if (value[i] != c) | ||
| 1331 | c = value[i]; | ||
| 1332 | else | ||
| 1333 | { | ||
| 1334 | //at least one repeating character | ||
| 1335 | return i - 1; | ||
| 1336 | } | ||
| 1337 | } | ||
| 1338 | else | ||
| 1339 | c = (char)0; | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | return -1; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | /// <summary> | ||
| 1346 | /// Searches a string for a repeated instance of the specified <paramref name="characterToFind"/>. | ||
| 1347 | /// </summary> | ||
| 1348 | /// <param name="value">The string to process.</param> | ||
| 1349 | /// <param name="characterToFind">The character of interest.</param> | ||
| 1350 | /// <returns>The index of the first instance of the character that is repeated or (-1) if no repeated chars found.</returns> | ||
| 1351 | public static int IndexOfRepeatedChar(this string value, char characterToFind) | ||
| 1352 | { | ||
| 1353 | return IndexOfRepeatedChar(value, characterToFind, 0); | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | /// <summary> | ||
| 1357 | /// Searches a string for an instance of a repeated character. | ||
| 1358 | /// </summary> | ||
| 1359 | /// <param name="value">The string to process.</param> | ||
| 1360 | /// <returns>The index of the first instance of any character that is repeated or (-1) if no repeated chars found.</returns> | ||
| 1361 | public static int IndexOfRepeatedChar(this string value) | ||
| 1362 | { | ||
| 1363 | return IndexOfRepeatedChar(value, 0); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | /// <summary> | ||
| 1367 | /// Searches a string for an instance of a repeated character from specified <paramref name="startIndex"/>. | ||
| 1368 | /// </summary> | ||
| 1369 | /// <param name="value">The string to process.</param> | ||
| 1370 | /// <param name="startIndex">The index from which to begin the search.</param> | ||
| 1371 | /// <returns>The index of the first instance of any character that is repeated or (-1) if no repeated chars found.</returns> | ||
| 1372 | public static int IndexOfRepeatedChar(this string value, int startIndex) | ||
| 1373 | { | ||
| 1374 | if (string.IsNullOrEmpty(value)) | ||
| 1375 | return -1; | ||
| 1376 | |||
| 1377 | if (startIndex < 0) | ||
| 1378 | return -1; | ||
| 1379 | |||
| 1380 | char c = (char)0; | ||
| 1381 | |||
| 1382 | for (int i = startIndex; i < value.Length; i++) | ||
| 1383 | { | ||
| 1384 | if (value[i] != c) | ||
| 1385 | c = value[i]; | ||
| 1386 | else | ||
| 1387 | { | ||
| 1388 | //at least one repeating character | ||
| 1389 | return i - 1; | ||
| 1390 | } | ||
| 1391 | } | ||
| 1392 | return -1; | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | /// <summary> | ||
| 1396 | /// Returns the index of the last repeated index of the first group of repeated characters that begin with the <paramref name="characterToFind"/>. | ||
| 1397 | /// </summary> | ||
| 1398 | /// <param name="value">String to process.</param> | ||
| 1399 | /// <param name="characterToFind">The character of interest.</param> | ||
| 1400 | /// <param name="startIndex">The index from which to begin the search.</param> | ||
| 1401 | /// <returns>The index of the last instance of the character that is repeated or (-1) if no repeated chars found.</returns> | ||
| 1402 | private static int LastIndexOfRepeatedChar(string value, char characterToFind, int startIndex) | ||
| 1403 | { | ||
| 1404 | if (startIndex > value.Length - 1) | ||
| 1405 | return -1; | ||
| 1406 | |||
| 1407 | int i = value.IndexOf(characterToFind, startIndex); | ||
| 1408 | |||
| 1409 | if (i == -1) | ||
| 1410 | return -1; | ||
| 1411 | |||
| 1412 | for (int j = i + 1; j < value.Length; j++) | ||
| 1413 | { | ||
| 1414 | if (value[j] != characterToFind) | ||
| 1415 | return j - 1; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | return value.Length - 1; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | /// <summary> | ||
| 1422 | /// Places an ellipsis in the middle of a string as it is trimmed to length specified. | ||
| 1423 | /// </summary> | ||
| 1424 | /// <param name="value">The string to process.</param> | ||
| 1425 | /// <param name="length">The maximum returned string length; mimimum value is 5.</param> | ||
| 1426 | /// <returns> | ||
| 1427 | /// A trimmed string of the specified <paramref name="length"/> or empty string if <paramref name="value"/> is null or empty. | ||
| 1428 | /// </returns> | ||
| 1429 | /// <remarks> | ||
| 1430 | /// Returned string is not padded to fill field length if <paramref name="value"/> is shorter than length. | ||
| 1431 | /// </remarks> | ||
| 1432 | public static string TrimWithEllipsisMiddle(this string value, int length) | ||
| 1433 | { | ||
| 1434 | if (string.IsNullOrEmpty(value)) | ||
| 1435 | return ""; | ||
| 1436 | |||
| 1437 | if (length < 5) | ||
| 1438 | length = 5; | ||
| 1439 | |||
| 1440 | value = value.Trim(); | ||
| 1441 | |||
| 1442 | if (value.Length <= length) | ||
| 1443 | return value; | ||
| 1444 | |||
| 1445 | int s1_Len = (int)(length / 2) - 1; | ||
| 1446 | |||
| 1447 | return string.Concat(value.Substring(0, s1_Len), "...", value.Substring(value.Length - s1_Len + 1 - length % 2)); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | /// <summary> | ||
| 1451 | /// Places an ellipsis at the end of a string as it is trimmed to length specified. | ||
| 1452 | /// </summary> | ||
| 1453 | /// <param name="value">The string to process.</param> | ||
| 1454 | /// <param name="length">The maximum returned string length; mimimum value is 5.</param> | ||
| 1455 | /// <returns> | ||
| 1456 | /// A trimmed string of the specified <paramref name="length"/> or empty string if <paramref name="value"/> is null or empty. | ||
| 1457 | /// </returns> | ||
| 1458 | /// <remarks> | ||
| 1459 | /// Returned string is not padded to fill field length if <paramref name="value"/> is shorter than length. | ||
| 1460 | /// </remarks> | ||
| 1461 | public static string TrimWithEllipsisEnd(this string value, int length) | ||
| 1462 | { | ||
| 1463 | if (string.IsNullOrEmpty(value)) | ||
| 1464 | return ""; | ||
| 1465 | |||
| 1466 | if (length < 5) | ||
| 1467 | length = 5; | ||
| 1468 | |||
| 1469 | value = value.Trim(); | ||
| 1470 | |||
| 1471 | if (value.Length <= length) | ||
| 1472 | return value; | ||
| 1473 | |||
| 1474 | return string.Concat(value.Substring(0, length - 3), "..."); | ||
| 1475 | } | ||
| 1476 | } | ||
| 1477 | } | ||