C# String Extension & Helper Methods


I enjoyed spending some time after creating some very useful (at least to me 😛 ) C# helper functions for string operation. So here is the gist of it, in a gist. If you do not know what is it doing, toy with it – I’d eventually add some XML comments after sanitizing them.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
// The indentation settings in the Gist do not seem to be listening to me. size: 4, mode: tabs, (w)rap: Atlanta hip hop
namespace WordPress.WildClick.Helpers
{
public class Helper
{
static public string BeautifyXml(string rawXmlString)
{
var sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = " ";
settings.NewLineChars = "\r\n";
settings.NewLineHandling = NewLineHandling.Replace;
using (XmlWriter writer = XmlWriter.Create(sb, settings))
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(rawXmlString);
xDoc.Save(writer);
}
return sb.ToString();
}
}
public static class ExtensionMethods
{
public static IEnumerable<T> Select<T>(this IDataReader reader, Func<IDataReader, T> projection)
{
while (reader.Read())
{
yield return projection(reader);
}
}
/// <summary>
/// Return null-able type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="s"></param>
/// <returns></returns>
public static Nullable<T> ToNullable<T>(this string s) where T : struct
{
Nullable<T> result = new Nullable<T>();
try
{
if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
result = (T)conv.ConvertFrom(s);
}
}
catch { } // Code sanitized, judge me anyway if you want
return result;
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
/// <summary>
/// Checks if a given string contains any of the substrings in the passed array of strings.
/// </summary>
/// <param name="theString"></param>
/// <param name="substrings"></param>
/// /// <param name="comparision"></param>
/// <returns>System.Boolean</returns>
public static bool ContainsAny(this string theString, string[] substrings, StringComparison comparision = StringComparison.Ordinal)
{
if (theString == null || (theString.Length == 0 && !substrings.Contains("")))
{
return false;
}
string substring;
for (int i = 0, N = substrings.Length; i < N; i++)
{
substring = substrings[i];
if (theString.IndexOf(substring, comparision) >= 0)
{
return true;
}
}
return false;
}
/// <summary>
/// exception free way of getting the substring
/// </summary>
/// <param name="theString"></param>
/// <param name="length"></param>
/// <returns>System.string</returns>
public static string SubString(this string theString, int length)
{
return SubString(theString, 0, length);
}
/// <summary>
/// exception free way of getting the substring
/// </summary>
/// <param name="theString"></param>
/// <param name="length"></param>
/// <param name="startIndex"></param>
/// <returns>System.string</returns>
public static string SubString(this string theString, int startIndex, int length)
{
if (theString == null || theString.Length == 0)
{
return "";
}
startIndex = Math.Abs(startIndex);
length = Math.Abs(length);
if (startIndex > theString.Length)
{
return "";
}
string substring = "";
if (theString.Length < length && startIndex == 0)
{
return theString;
}
length = Math.Min(startIndex + length, theString.Length) startIndex;
substring = theString.Substring(startIndex, length);
return substring;
}
/// <summary>
/// Chops the string by making sure that the words does not cut off
/// </summary>
/// <param name="theString"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <returns>System.string</returns>
public static string ChopString(this string theString, int start, int length)
{
if (theString == null || theString.Length == 0)
{
return "";
}
theString = theString.Trim();
while (start != 0 && start < theString.Length && theString[start 1] != ' ')
{
start++;
}
if (start > theString.Length)
{
return "";
}
theString = theString.Substring(start);
string[] words = theString.Split(new char[] { ' ' });
words = words.Where(w => w != "").ToArray<string>();
if (words.Length > 0)
{
if (words[0].Length == length)
{
return words[0];
}
}
StringBuilder sbChoppedString = new StringBuilder();
for (int i = 0; i < words.Length && (sbChoppedString.Length + words[i].Length + 1) <= length; i++)
{
sbChoppedString.Append(words[i] + " ");
}
if (sbChoppedString.Length > 0)
{
sbChoppedString.Length;
}
return sbChoppedString.ToString().TrimEnd();
}
/// <summary>
/// ChopString v2.0. This version let's you specify the string, which separates two words
/// </summary>
/// <param name="theString"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <param name="delimiterString"></param>
/// <returns></returns>
public static string ChopString2(this string theString, int start, int length, string delimiterString = " ")
{
if (theString == null || theString.Length == 0)
{
return "";
}
theString = theString.Trim();
while (start != 0 && start < theString.Length && theString[start 1] != ' ')
{
start++;
}
if (start > theString.Length)
{
return "";
}
theString = theString.Substring(start);
if (delimiterString == null)
{
delimiterString = " ";
}
char[] delimiter = delimiterString.ToCharArray();
string[] words = theString.Split(delimiter);
words = words.Where(w => w != "").ToArray<string>();
if (words.Length > 0)
{
if (words[0].Length == length)
return words[0];
}
StringBuilder sbChoppedString = new StringBuilder();
for (int i = 0; i < words.Length && (sbChoppedString.Length + words[i].Length + 1) <= length; i++)
{
sbChoppedString.Append(words[i] + delimiterString);
}
if (sbChoppedString.Length > 0)
{
sbChoppedString.Length -= delimiterString.Length;
}
return sbChoppedString.ToString().TrimEnd();
}
/// <summary>
/// Breaks a long string into specified number of smaller string chunk with or without breaking the words
/// </summary>
/// <param name="theString">The long string which you wanna break down into N chunks of specified width</param>
/// <param name="chunkLength">The desired width of each string chunk</param>
/// <param name="count">Count of maximum string chunks ( ignores the rest )</param>
/// <returns></returns>
public static IEnumerable<string> WrapLine(this string theString, int chunkLength)
public static IEnumerable<string> WrapLine(this string theString, int chunkLength, int version = 1)
{
// Syntactic Sugar for calling the actual extension method with default parameters
return WrapLine(theString, chunkLength, (int?)null, false, true);
// syntactic sugar for calling the actual extension method with default parameters
return WrapLine(theString, chunkLength, (int?)null, false, true, version: version);
}
/// <summary>
/// Breaks a long string into specified number of smaller string chunk with or without breaking the words + more options
/// </summary>
/// <param name="theString">The long string which you wanna break down into N chunks of specified width</param>
/// <param name="chunkLength">The desired width of each string chunk</param>
/// <param name="count">Count of maximum string chunks, (int?)Null for all( ignores the rest )</param>
/// <param name="breakWord">Flag to specify whether to let the last word break on each segment if it exceeds the specified segment length</param>
/// <param name="trimChunks">Trim the white space from both the ends of all string chunk</param>
/// <param name="truncateLastLine">If set, and iff count has been specified, the last string chunk might break word to fit in max data</param>
/// <returns></returns>
public static IEnumerable<string> WrapLine(this string theString, int chunkLength, int? count, bool breakWord = false, bool trimChunks = true, bool truncateLastLine = false)
public static IEnumerable<string> WrapLine(this string theString, int chunkLength, int? count, bool breakWord = false, bool trimChunks = true, bool truncateLastLine = false, int version = 1)
{
// Simon, Go Back !
if (theString == null || theString.Length == 0)
{
return Enumerable.Empty<string>();
}
if (count.HasValue && count.Value <= 0)
{
return Enumerable.Empty<string>();
}
// if yer chunkLength is smaller than the longest word then – can't fit that in, fella !
int longestWordLength = theString
.Split(' ')
.Aggregate("", (max, current) => max.Length > current.Length ? max : current)
.Length;
if (chunkLength < longestWordLength)
{
return Enumerable.Empty<string>();
}
StringBuilder sbTheString = new StringBuilder(theString.Replace(" ", " "));
List<string> carryForwards = new List<string>();
string lastString = null, currentString = null;
bool flag = false;
if (!breakWord)
{
do
{
if (count.HasValue && carryForwards.Count == count)
{
break;
}
while (sbTheString.Length > 0 && sbTheString[0] == ' ')
{
sbTheString.Remove(0, 1);
}
if (truncateLastLine && count.HasValue && (carryForwards.Count == count 1))
{
currentString = sbTheString.ToString().SubString(flag ? 1 : 0, chunkLength);
}
else
{
currentString = sbTheString.ToString().ChopString(flag ? 1 : 0, chunkLength);
if (version == 2)
{
currentString = sbTheString.ToString().ChopString2(flag ? 1 : 0, chunkLength);
}
else
{
currentString = sbTheString.ToString().ChopString(flag ? 1 : 0, chunkLength);
}
}
flag = carryForwards.Count > 0
&& (lastString != null && lastString.Length > 0)
&& (currentString != null && currentString.Length > 0)
&& (lastString.Last() == currentString[0])
&& ((theString.IndexOf(lastString) + lastString.Length) < theString.Length)
&& (theString[theString.IndexOf(lastString) + lastString.Length] == ' ' && theString[theString.IndexOf(lastString) + lastString.Length 1] == currentString[0]);
if (flag)
{
currentString = currentString.Substring(1);
}
carryForwards.Add(currentString);
lastString = currentString;
sbTheString.Remove(0, carryForwards.Last().Length);
}
while (sbTheString.Length > 0);
}
else
{
do
{
carryForwards.Add(sbTheString.ToString().Substring(0, Math.Min(chunkLength, sbTheString.Length)));
sbTheString.Remove(0, carryForwards.Last().Length);
}
while (sbTheString.Length > 0);
}
carryForwards = carryForwards.Where(c => c != "").ToList();
// shave those hairs !
// shave yer beard !
if (trimChunks)
{
carryForwards = carryForwards.Select(c => c = c.Trim()).ToList();
}
return carryForwards;
}
/// <summary>
/// Safely inserts a string at the end with making sure that the total length does not exceed the specified max length
/// </summary>
/// <param name="theString"></param>
/// <param name="value">string to append safely</param>
/// <param name="maxLength">max length</param>
/// <returns></returns>
public static string SafeAppend(this string theString, string value, int maxLength)
{
if (theString == null || theString.Length == 0)
{
return "";
}
if (value == null || value.Length == 0)
{
return theString.Substring(0, Math.Min(maxLength, theString.Length));
}
if (theString.Length + value.Length <= maxLength)
{
return (theString + value);
}
theString = theString += value;
return theString.Substring(0, Math.Min(theString.Length, maxLength));
}
public static string CompleteAppend(this string theString, string value, int maxLength)
{
int numberOfAvailableSpaces = maxLength theString.Length;
int numberOfCharactersToRemove = (value.Length + 1) numberOfAvailableSpaces;
string newString;
if (numberOfCharactersToRemove > 1)
{
newString = theString.Remove(theString.Length numberOfCharactersToRemove);
}
else
{
newString = theString;
}
return String.Format("{0} {1}", newString, value);
}
/// <summary>
/// returns index of an item in the list regardless of the case
/// </summary>
/// <param name="list"></param>
/// <param name="item"></param>
/// <returns>System.Int32</returns>
public static int IndexOfCaseInsensitive(this List<string> list, string item)
{
int index = 1;
index = list.FindIndex(p => string.Equals(p, item, StringComparison.OrdinalIgnoreCase));
return (index == 1 ? list.Count : index);
}
/// <summary>
/// returns string with only numeric characters, filtering everything else
/// </summary>
/// <param name="theString"></param>
/// <returns></returns>
public static string OnlyNumeric(this string theString, bool keepSpace = true)
{
if (theString == null || theString.Length == 0)
{
return "";
}
StringBuilder sb = new StringBuilder();
foreach (var c in theString)
{
if ((c == ' ' && keepSpace) || (c >= '0' && c <= '9'))
{
sb.Append(c);
}
}
return sb.Length == 0 ? "" : sb.ToString().Replace(" ", " ").Trim(new char[] { ' ' });
}
/// <summary>
/// returns string with only alphabetical characters, filtering everything else
/// </summary>
/// <param name="theString"></param>
/// <returns></returns>
public static string OnlyAlpha(this string theString, bool keepSpace = true)
{
if (theString == null || theString.Length == 0)
{
return "";
}
StringBuilder sb = new StringBuilder();
foreach (var c in theString)
{
if ((c == ' ' && keepSpace) || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
{
sb.Append(c);
}
}
return sb.Length == 0 ? "" : sb.ToString().Replace(" ", " ").Trim(new char[] { ' ' });
}
/// <summary>
/// returns string with only Alpha-Numeric characters, filtering everything else
/// </summary>
/// <param name="theString"></param>
/// <returns></returns>
public static string OnlyAlphaNumeric(this string theString, bool keepSpace = true)
{
if (theString == null || theString.Length == 0)
{
return "";
}
StringBuilder sb = new StringBuilder();
foreach (var c in theString)
{
if ((c == ' ' && keepSpace) || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
{
sb.Append(c);
}
}
return sb.Length == 0 ? "" : sb.ToString().Replace(" ", " ").Trim(new char[] { ' ' });
}
/// <summary>
/// Finds all occurrences of substrings specified in an array and replaces all those substrings with a specified value. Returns null for null or empty string.
/// </summary>
/// <param name="theString"></param>
/// <param name="subStringsToReplace">All targeted substrings (null and empty strings are ignored), returns original string back if not specified / null</param>
/// <param name="newValue">String be replaced for targeted substrings, Default is "" and if set to null the original string is bounced back</param>
/// <returns></returns>
public static string ReplaceAll(this string theString, string[] subStringsToReplace = null, string newValue = "")
{
if (theString == null || theString.Length == 0)
{
return "";
}
if (subStringsToReplace == null || subStringsToReplace.Length == 0 || newValue == null)
{
return theString;
}
if (!theString.ContainsAny(subStringsToReplace))
{
return theString;
}
var stringsToReplace = subStringsToReplace
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList<string>()
.Where(x => x != null || x.Length > 0);
foreach (var oldValue in stringsToReplace)
{
StringBuilder sb;
var index = theString.IndexOf(oldValue, 0, theString.Length, StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
// Seems like string.Replace should have an overload that takes a StringComparison argument. Since it doesn't I am using following
sb = new StringBuilder();
int previousIndex = 0;
while (index != 1)
{
sb.Append(theString.Substring(previousIndex, index previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = theString.IndexOf(oldValue, index, StringComparison.OrdinalIgnoreCase);
}
sb.Append(theString.Substring(previousIndex));
theString = sb.ToString();
}
}
return theString;
}
/// <summary>
/// Get CSV
/// </summary>
/// <typeparam name="T">Generic</typeparam>
/// <param name="list">Generic List</param>
/// <param name="withHeaders">true, if returning with header names; otherwise, false</param>
/// <returns>CSV as a String</returns>
public static string GetCSV<T>(this List<T> list, bool withHeaders = false)
{
StringBuilder sb = new StringBuilder();
//Get the properties for type T for the headers
PropertyInfo[] propertyInfos = typeof(T).GetProperties();
if (withHeaders)
{
for (int i = 0; i <= propertyInfos.Length 1; i++)
{
sb.Append(propertyInfos[i].Name);
if (i < propertyInfos.Length 1)
{
sb.Append("^");
}
}
sb.AppendLine();
}
//Loop through the collection, then the properties and add the values
for (int i = 0; i <= list.Count 1; i++)
{
T item = list[i];
for (int j = 0; j <= propertyInfos.Length 1; j++)
{
object o = item.GetType().GetProperty(propertyInfos[j].Name).GetValue(item, null);
if (o != null)
{
string value = o.ToString();
////Check if the value contains a comma and place it in quotes if so
//if (value.Contains(","))
//{
// value = string.Concat("\"", value, "\"");
//}
//Replace any \r or \n special characters from a new line with a space
if (value.Contains("\r"))
{
value = value.Replace("\r", " ");
}
if (value.Contains("\n"))
{
value = value.Replace("\n", " ");
}
sb.Append(value);
}
if (j < propertyInfos.Length 1)
{
sb.Append("^");
}
}
sb.AppendLine();
}
return sb.ToString().TrimEnd();
}
}
}

https://gist.github.com/ablaze8/16e36c2ee60b9eacad395cda0f801b74

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s