Click here to Skip to main content
Click here to Skip to main content

String Extension Collection for C#

, 27 Nov 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Several useful extensions for System.String.

Introduction

Extension methods (a new feature of C# 3.0) are useful as they enable to "add" methods to a class without modifying its source code. Such methods behave (from a point of writing code and intellisense) like member methods. This is very useful for built-in .NET classes or third-party libraries. Hundreds of articles have been written about this; the aim of this article is not to introduce extension methods, but to show a collection of several most useful extension methods for the System.String class.

This article brings a small library (a code file and unit tests for this code). Some of the extension methods have been collected from various websites, and some were written by me. Unit tests are presented for demonstration purposes.

Background

For those who don't know about extension methods, I suggest reading this nice article on Wikipedia.

Using the Code

Let me introduce the source code without much delay. The first method was written by David Hayden and checks if an email ID is in valid format.

/// <summary>
/// true, if is valid email address
/// from http://www.davidhayden.com/blog/dave/
/// archive/2006/11/30/ExtensionMethodsCSharp.aspx
/// </summary>
/// <param name="s">email address to test</param>
/// <returns>true, if is valid email address</returns>

public static bool IsValidEmailAddress(this string s)
{
    return new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,6}$").IsMatch(s);
}

The counterpart test method is the following:

[TestMethod()]
public void IsValidEmailAddressTest()
{
    Assert.IsTrue("yellowdog@someemail.uk".IsValidEmailAddress());
    Assert.IsTrue("yellow.444@email4u.co.uk".IsValidEmailAddress());
    Assert.IsFalse("adfasdf".IsValidEmailAddress());
    Assert.IsFalse("asd@asdf".IsValidEmailAddress());
}

I have found a lot of UR validation functions, but not all of them seemed to be OK. This method is inspired by a Regular Expression written by bb, which seems to work fine.

/// <summary>
/// Checks if url is valid. 
/// from http://www.osix.net/modules/article/?id=586
/// and changed to match http://localhost
/// 
/// complete (not only http) url regex can be found 
/// at http://internet.ls-la.net/folklore/url-regexpr.html
/// </summary>
/// <param name="text"></param>

/// <returns></returns>
public static bool IsValidUrl(this string url)
{
    string strRegex = "^(https?://)"
+ "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //user@
+ @"(([0-9]{1,3}\.){3}[0-9]{1,3}" // IP- 199.194.52.184
+ "|" // allows either IP or domain
+ @"([0-9a-z_!~*'()-]+\.)*" // tertiary domain(s)- www.
+ @"([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]" // second level domain
+ @"(\.[a-z]{2,6})?)" // first level domain- .com or .museum is optional
+ "(:[0-9]{1,5})?" // port number- :80
+ "((/?)|" // a slash isn't required if there is no file name
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
    return new Regex(strRegex).IsMatch(url);
}

The counterpart test method is the following:

/// <summary>
///A test for IsValidUrl
///</summary>
[TestMethod()]
public void IsValidUrlTest()
{
    Assert.IsTrue("http://www.codeproject.com".IsValidUrl());
    Assert.IsTrue("https://www.codeproject.com/#some_anchor".IsValidUrl());
    Assert.IsTrue("https://localhost".IsValidUrl());
    Assert.IsTrue("http://www.abcde.nf.net/signs-banners.jpg".IsValidUrl());
    Assert.IsTrue("http://aa-bbbb.cc.bla.com:80800/test/" + 
                  "test/test.aspx?dd=dd&id=dki".IsValidUrl());
    Assert.IsFalse("http:wwwcodeprojectcom".IsValidUrl());
    Assert.IsFalse("http://www.code project.com".IsValidUrl());
}

I have written a third method to test if the user provides the existing homepage:

/// <summary>
/// Check if url (http) is available.
/// </summary>
/// <param name="httpUri">url to check</param>
/// <example>

/// string url = "www.codeproject.com;
/// if( !url.UrlAvailable())
///     ...codeproject is not available
/// </example>
/// <returns>true if available</returns>
public static bool UrlAvailable(this string httpUrl)
{
    if (!httpUrl.StartsWith("http://") || !httpUrl.StartsWith("https://"))
        httpUrl = "http://" + httpUrl;
    try
    {
        HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(httpUrl);
        myRequest.Method = "GET";
        myRequest.ContentType = "application/x-www-form-urlencoded";
        HttpWebResponse myHttpWebResponse = 
           (HttpWebResponse)myRequest.GetResponse();
        return true;
    }
    catch
    {
        return false;
    } 
}

The counterpart test method is the following:

public void UrlAvailableTest()
{
    Assert.IsTrue("www.codeproject.com".UrlAvailable());
    Assert.IsFalse("www.asjdfalskdfjalskdf.com".UrlAvailable());
}

The reversing string example can be found on Wikipedia. This version without the cycle looks better.

/// <summary>

/// Reverse the string
/// from http://en.wikipedia.org/wiki/Extension_method
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}

The counterpart test method is as follows:

public void ReverseTest()
{
    string input = "yellow dog";
    string expected = "god wolley";
    string actual = input.Reverse();
    Assert.AreEqual(expected, actual);
}

Sometimes, you need to provide a preview of a long text. This can be done using this Reduce extension method:

/// <summary>

/// Reduce string to shorter preview which is optionally ended by some string (...).
/// </summary>
/// <param name="s">string to reduce</param>
/// <param name="count">Length of returned string including endings.</param>
/// <param name="endings">optional edings of reduced text</param>

/// <example>
/// string description = "This is very long description of something";
/// string preview = description.Reduce(20,"...");
/// produce -> "This is very long..."
/// </example>
/// <returns></returns>

public static string Reduce(this string s, int count, string endings)
{
    if (count < endings.Length)
        throw new Exception("Failed to reduce to less then endings length.");
    int sLength = s.Length;
    int len = sLength;
    if (endings != null)
        len += endings.Length;
    if (count > sLength)
        return s; //it's too short to reduce
    s = s.Substring(0, sLength - len + count);
    if (endings != null)
        s += endings;
    return s;
}

The counterpart test method is the following:

[TestMethod()]
public void ReduceTest()
{
    string input = "The quick brown fox jumps over the lazy dog";
    int count = 10; 
    string endings = "...";
    string expected = "The qui...";
    string actual = input.Reduce(count, endings);
    Assert.AreEqual(expected, actual);
}

Sometimes you need to parse a phone number or a price, and the user might have interposed the string with spaces. To not boss the user about, and to avoid duplicating test conditions, you can use the RemoveSpaces extension method when parsing numbers.

/// <summary>
/// remove white space, not line end
/// Useful when parsing user input such phone,
/// price int.Parse("1 000 000".RemoveSpaces(),.....
/// </summary>
/// <param name="s"></param>

/// <param name="value">string without spaces</param>
public static string RemoveSpaces(this string s)
{
    return s.Replace(" ", "");
}

The counterpart test method is the following:

[TestMethod()]
public void RemoveSpacesTest()
{
    string input = "yellow dog" + Environment.NewLine  + "black cat";
    string expected = "yellowdog" + Environment.NewLine + "blackcat";
    string actual = input.RemoveSpaces();
    Assert.AreEqual(expected, actual);
}

If you need to ensure the user input to be a number and you want to be tolerant of the number format, use the IsNumber extension.

/// <summary>
/// true, if the string can be parse as Double respective Int32
/// Spaces are not considred.
/// </summary>
/// <param name="s">input string</param>

/// <param name="floatpoint">true, if Double is considered,
/// otherwhise Int32 is considered.</param>
/// <returns>true, if the string contains only digits or float-point</returns>
public static bool IsNumber(this string s, bool floatpoint)
{
    int i;
    double d;
    string withoutWhiteSpace = s.RemoveSpaces();
    if (floatpoint)
        return double.TryParse(withoutWhiteSpace, NumberStyles.Any,
            Thread.CurrentThread.CurrentUICulture , out d);
    else
        return int.TryParse(withoutWhiteSpace, out i);
}

The counterpart test method is the following:

[TestMethod()]
public void IsNumberTest()
{
    Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

    Assert.IsTrue("12345".IsNumber(false));
    Assert.IsTrue("   12345".IsNumber(false));
    Assert.IsTrue("12.345".IsNumber(true));
    Assert.IsTrue("   12,345 ".IsNumber(true));
    Assert.IsTrue("12 345".IsNumber(false));
    Assert.IsFalse("tractor".IsNumber(true));
}

The more restrictive version of the IsNumber method is IsNumberOnly, which ensures that all characters are digits, possibly float point. This could also be done using LINQ via s.ToCharArray().Where(...).Count() == 0.

/// <summary>
/// true, if the string contains only digits or float-point.
/// Spaces are not considred.
/// </summary>
/// <param name="s">input string</param>

/// <param name="floatpoint">true, if float-point is considered</param>
/// <returns>true, if the string contains only digits or float-point</returns>
public static bool IsNumberOnly(this string s, bool floatpoint)
{
    s = s.Trim();
    if (s.Length == 0)
        return false;
    foreach (char c in s)
    {
        if (!char.IsDigit(c))
        {
            if (floatpoint && (c == '.' || c == ','))
                continue;
            return false;
        }
    }
    return true;
}

The counterpart test method is the following:

[TestMethod()]
public void IsNumberOnlyTest()
{
    Assert.IsTrue("12345".IsNumberOnly(false));
    Assert.IsTrue("   12345".IsNumberOnly(false));
    Assert.IsTrue("12.345".IsNumberOnly(true));
    Assert.IsTrue("   12,345 ".IsNumberOnly(true));
    Assert.IsFalse("12 345".IsNumberOnly(false));
    Assert.IsFalse("tractor".IsNumberOnly(true));
}

Michael Kaplan describes a very useful method for removing diacritics (accents) from strings. It is useful when implementing URL rewriting, and you need to generate valid and readable URLs.

/// <summary>
/// Remove accent from strings 
/// </summary>
/// <example>
///  input:  "Příliš žluťoučký kůň úpěl ďábelské ódy."
///  result: "Prilis zlutoucky kun upel dabelske ody."
/// </example>
/// <param name="s"></param>
/// <remarks>founded at http://stackoverflow.com/questions/249087/
/// how-do-i-remove-diacritics-accents-from-a-string-in-net</remarks>
/// <returns>string without accents</returns>

public static string RemoveDiacritics(this string s)
{
    string stFormD = s.Normalize(NormalizationForm.FormD);
    StringBuilder sb = new StringBuilder();

    for (int ich = 0; ich < stFormD.Length; ich++)
    {
        UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);
        if (uc != UnicodeCategory.NonSpacingMark)
        {
            sb.Append(stFormD[ich]);
        }
    }
    return (sb.ToString().Normalize(NormalizationForm.FormC));
}

The counterpart test method is the following:

/// <summary>
///A test for RemoveDiacritics
///</summary>
[TestMethod()]
public void RemoveDiacriticsTest()
{
    //contains all czech accents
    ///  input:  "Příliš žluťoučký kůň úpěl ďábelské ódy."
    ///  result: "Prilis zlutoucky kun upel dabelske ody."
    string actual = input.RemoveDiacritics();
    Assert.AreEqual(expected, actual);
}

When I was programming in PHP, Nl2Br was a very useful PHP function. This one was posted by DigiMortal.

/// <summary>
/// Replace \r\n or \n by <br />
/// from http://weblogs.asp.net/gunnarpeipman/archive/2007/11/18/c-extension-methods.aspx
/// </summary>

/// <param name="s"></param>
/// <returns></returns>
public static string Nl2Br(this string s)
{
    return s.Replace("\r\n", "<br />").Replace("\n", "<br />");
}

The counterpart test method is the following:

[TestMethod()]
public void Nl2BrTest()
{
    string input = "yellow dog" + Environment.NewLine + "black cat";
    string expected = "yellow dog<br />black cat";
    string actual = input.Nl2Br();
    Assert.AreEqual(expected, actual);
}

The MD5 function can be used in almost every application.

/// <summary>
static MD5CryptoServiceProvider s_md5 = null;

/// from http://weblogs.asp.net/gunnarpeipman/archive/2007/11/18/c-extension-methods.aspx
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string MD5(this string s)
{
    if( s_md5 == null) //creating only when needed
        s_md5 = new MD5CryptoServiceProvider();
    Byte[] newdata = Encoding.Default.GetBytes(s);
    Byte[] encrypted = s_md5.ComputeHash(newdata);
    return BitConverter.ToString(encrypted).Replace("-", "").ToLower();
}

The counterpart test method is the following:

[TestMethod()]
public void MD5Test()
{
    string input = "The quick brown fox jumps over the lazy dog";
    string expected = "9e107d9d372bb6826bd81d3542a419d6";
    string actual = input.MD5();
    Assert.AreEqual(expected, actual);
}

Points of Interest

While writing this article, I have found an extensive library here. Unfortunately, some links don't work.

History

  • 18 Nov 2008
    • Definition of extension methods was changed not to propagate a misstatement about them being members.
    • Removed CreateDirIfNotExistsTest, it was really useless.
    • URL Regex changed to match http://localhost
  • 25 Nov 2008
    • Changed Invariant Culture to CurrentUI Culture (thanks to x2develop.com).
    • Changed email regex to match .museum domain.
    • Changed MD5 method to be more effective (thanks to Juan).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tomas Kubes
Other my own project
Czech Republic Czech Republic
My name is Tomas Kubes, feel free to contact me at tomas_kubes(at)seznam.cz

Comments and Discussions

 
Generalbug report! PinmemberBehrooz_cs25-Aug-10 7:19 
Generaldo not use throw new Exception(... Pinmemberpetr1234520-Jul-10 0:17 
GeneralMy vote of 4 PinmemberPaw Jershauge19-Jan-10 6:35 
GeneralMy vote of 1 PinmemberFrederic Sivignon13-Sep-09 23:57 
GeneralRe: My vote of 1 PinmemberPaw Jershauge19-Jan-10 6:29 
GeneralRe: My vote of 1 PinmemberChris G.18-Aug-13 2:26 
SuggestionRe: My vote of 1 PinprofessionalPaw Jershauge19-Aug-13 4:31 
GeneralMy vote of 1 Pinmemberjachymko23-May-09 16:37 
GeneralRe: My vote of 1 PinmemberPaw Jershauge19-Jan-10 6:31 
GeneralGood article PinmemberDonsw8-Feb-09 7:05 
GeneralMy vote of 2 PinmemberRasqual Twilight6-Dec-08 2:00 
GeneralRe: My vote of 2 PinmemberPaw Jershauge19-Jan-10 6:32 
GeneralStatic Regex.IsMatch() method PinmemberTobiasP4-Dec-08 4:47 
GeneralBullet list of methods in the Introduction PinmemberHC7225-Nov-08 23:43 
GeneralAbout MD5 Pinmemberichramm25-Nov-08 2:39 
GeneralRe: About MD5 PinmemberTobiasP27-Nov-08 2:37 
GeneralRe: About MD5 PinmemberTomas Kubes27-Nov-08 6:38 
GeneralIsNumber is using InvariantCulture Pinmemberx2develop.com22-Nov-08 13:49 
GeneralRe: IsNumber is using InvariantCulture PinmemberTomas Kubes22-Nov-08 16:40 
GeneralRe: IsNumber is using InvariantCulture Pinmemberx2develop.com22-Nov-08 23:44 
GeneralRe: IsNumber is using InvariantCulture PinmemberTobiasP27-Nov-08 2:04 
GeneralRe: IsNumber is using InvariantCulture PinmemberTomas Kubes27-Nov-08 6:22 
Question....and why not? PinmemberRob Philpott18-Nov-08 6:06 
AnswerRe: ....and why not? PinmemberRamon Smits18-Nov-08 23:58 
GeneralRe: ....and why not? PinmemberTomas Kubes21-Nov-08 12:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 27 Nov 2008
Article Copyright 2008 by Tomas Kubes
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid