History

Renamed folders and separated GSF.Media and GSF.Net into separate projects.

StephenCWills committed 14 years ago

Commit ac1ef661 parent 45bce55628 files · +733 −461Browse files

Showing the changes to Source/Libraries/GSF.Core/StringExtensions.cs (1 of 628 files). Show all

Unified · Split

Source/Libraries/GSF.Core/StringExtensions.cs+1477−0
@@ -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
110using System;
111using System.Collections.Generic;
112using System.ComponentModel;
113using System.Globalization;
114using System.IO;
115using System.Text;
116using System.Text.RegularExpressions;
117
118namespace 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 (\ / : * ? " &lt; &gt; |) 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 (\ / : * ? " &lt; &gt; |) 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}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t