Click here to Skip to main content
14,084,843 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

45.2K views
882 downloads
31 bookmarked
Posted 5 Dec 2013
Licenced CPOL

C# String Extensions

, 5 Dec 2013
Rate this:
Please Sign up or sign in to vote.
Some commonly used operations with string in c# as extension methods

Introduction  

It is common we C# developers most of the time manipulate string values in our program for various reasons especially when it comes from user input, database or files etc. For example, we convert a string to integer when a user fills age value in a text box. The String class offers ConvertXxx methods for conversions like this but it lacks in flexibility and control, I think .  I have just consolidated all the extensions method I wrote against String class depends on the requirement at various times. Sure enough there are different ways you can achieve what I did using extension methods but I hope you will find them easy and extensible. 

Background  

Using built-in string methods can help us get the desired result but you repeat the code everywhere in need and I felt isolating the logic to one place will help the logic maintainable and testable.

Using the code

There are only two classes you can look at in the source code attached here : StringExtensions and StringExtensionsTest.The first class has all the extension methods for string class and the second class will test each extension methods with different scenarios to ensure the desired output returned to the user. 


These are the 13 extension methods.

We will look at each method and few test methods to verify the result (there are 59 test methods in total which you can look at it by downloading the source code) 

Note : I named the test method as Roy Osherove suggested in his book The art of Unit Testing. That is [methodUnderTest]_[Scenario]_[Expected].

Example

public void toDate_hasValidDate_Date()

Extension methods

1.toDate

Accepts a string and convert as DateTime. It has an optional parameter throwExceptionIfFailed. if it is true then caller of this method should expect exception and handle it. Default is false which means that it returns DateTime.MinValue if the string cannot be converted as DateTime

public static DateTime toDate(this string input, bool throwExceptionIfFailed = false)
{
    DateTime result;
    var valid = DateTime.TryParse(input, out result);
    if (!valid)
        if (throwExceptionIfFailed)
            throw new FormatException(string.Format("'{0}' cannot be converted as DateTime", input));
    return result;
}

And the test methods:

[TestMethod]
public void toDate_hasValidDate_Date()
{
    Assert.AreEqual(DateTime.Parse("2013-12-05"), "2013-12-05".toDate());
}

[TestMethod]
public void toDate_hasInvalidDate_MinDate()
{
    Assert.AreEqual(DateTime.MinValue, "2013-50-70".toDate());
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void toDate_notADateStringButWantException_FormatException()
{
    "abcd".toDate(true);
}

2.toInt

public static int toInt(this string input, bool throwExceptionIfFailed = false)
{
    int result;
    var valid = int.TryParse(input, out result);
    if (!valid)
        if (throwExceptionIfFailed)
            throw new FormatException(string.Format("'{0}' cannot be converted as int", input));
    return result;
}

And the test methods

[TestMethod]
public void toInt_notANumber_0()
{
    Assert.AreEqual(0, "abcd".toInt());
}

[TestMethod]
public void toInt_10_10()
{
    Assert.AreEqual(10, "10".toInt());
}

[TestMethod]
public void toInt_alphaNumeric_0()
{
    Assert.AreEqual(0, "a10b34c".toInt());
}

3.toDouble

public static double toDouble(this string input, bool throwExceptionIfFailed = false)
{
    double result;
    var valid = double.TryParse(input, NumberStyles.AllowDecimalPoint, 
      new NumberFormatInfo { NumberDecimalSeparator = "." }, out result);
    if (!valid)
        if (throwExceptionIfFailed)
            throw new FormatException(string.Format("'{0}' cannot be converted as double", input));
    return result;
}

And the test methods

[TestMethod]
public void toDouble_notANumber_0()
{
    Assert.AreEqual(0, "abcd".toDouble());
}

[TestMethod]
public void toDouble_10point5_10point5()
{
    Assert.AreEqual(10.5, "10.5".toDouble());
}

4.reverse

public static string reverse(this string input)
{
    if (string.IsNullOrWhiteSpace(input)) return string.Empty;
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}

And the test methods

[TestMethod]
public void reverse_empty_empty()
{
    Assert.AreEqual("", "".reverse());
}

[TestMethod]
public void reverse_abcd_dcba()
{
    Assert.AreEqual("dcba", "abcd".reverse());
}

5.toSentence

/// <summary>
/// Matching all capital letters in the input and seperate them with spaces to form a sentence.
/// If the input is an abbreviation text, no space will be added and returns the same input.
/// </summary>
/// <example>
/// input : HelloWorld
/// output : Hello World
/// </example>
/// <example>
/// input : BBC
/// output : BBC
/// </example>
/// <param name="input" />
/// <returns>
public static string toSentence(this string input)
{
    if (string.IsNullOrWhiteSpace(input))
        return input;
    //return as is if the input is just an abbreviation
    if (Regex.Match(input, "[0-9A-Z]+$").Success)
        return input;
    //add a space before each capital letter, but not the first one.
    var result = Regex.Replace(input, "(\\B[A-Z])", " $1");
    return result;
}

And the test methods

[TestMethod]
public void toSentence_OneCapLetter_OneSpace()
{
    Assert.AreEqual("Hello World", "HelloWorld".toSentence());
}

[TestMethod]
public void toSentence_5CapLetters_5Spaces()
{
    Assert.AreEqual("Such A Long Text To Test", 
      "SuchALongTextToTest".toSentence());
}

[TestMethod]
public void toSentence_NoCapLetter_AsIs()
{
    Assert.AreEqual("test", "test".toSentence());
}

One sample usage of this method. Consider this Enum and you print them using this extension

enum Task
{
    Code,
    Test,
    BugFix,
    ProdDep,
    DbDesign,
    ScreenDesign,
    TDD
}
var tasks = Enum.GetValues(typeof(Task));
foreach (var task in tasks)
{
    Console.WriteLine(task.ToString().toSentence());
}

Result

Code
Test
Bug Fix
Prod Dep
Db Design
Screen Design
TDD

6.getLast

public static string getLast(this string input, int howMany)
{
    if (string.IsNullOrWhiteSpace(input)) return string.Empty;
    var value = input.Trim();
    return howMany >= value.Length ? value : value.Substring(value.Length - howMany);
}

And the test methods

[TestMethod]
public void getLast_2CharectersButAskFor3_2charecter()
{
    Assert.AreEqual("ab", "ab".getLast(3));
}
[TestMethod]
public void getLast_2CharectersAndAskFor2_2charecter()
{
    Assert.AreEqual("ab", "ab".getLast(2));
}
[TestMethod]
public void getLast_4CharectersAndAskFor2_last2charecter()
{
    Assert.AreEqual("cd", "abcd".getLast(2));
}

7.getFirst

public static string getFirst(this string input, int howMany)
{
    if (string.IsNullOrWhiteSpace(input)) return string.Empty;
    var value = input.Trim();
    return howMany >= value.Length ? value : input.Substring(0, howMany);
}

And the test methods

[TestMethod]
public void getFirst_2CharectersButAskFor3_2charecter()
{
    Assert.AreEqual("ab", "ab".getFirst(3));
}
[TestMethod]
public void getFirst_2CharectersAndAskFor2_2charecter()
{
    Assert.AreEqual("ab", "ab".getFirst(2));
}
[TestMethod]
public void getFirst_4CharectersAndAskFor2_first2charecter()
{
    Assert.AreEqual("ab", "abcd".getFirst(2));
}

8.isEmail

public static bool isEmail(this string input)
{
    var match = Regex.Match(input, 
      @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
    return match.Success;
}

And the test methods

[TestMethod]
public void isEmail_notEmail_false()
{
    Assert.AreEqual(false, "1234".isEmail());
}

[TestMethod]
public void isEmail_mail_true()
{
    Assert.AreEqual(true, "abc@abc.com".isEmail());
} 

9.isPhone

public static bool isPhone(this string input)
{
    var match = Regex.Match(input, 
      @"^\+?(\d[\d-. ]+)?(\([\d-. ]+\))?[\d-. ]+\d$", RegexOptions.IgnoreCase);
    return match.Success;
} 

And the test methods

[TestMethod]
public void isPhone_ValidNumberPrefixedPlus_true()
{
    Assert.AreEqual(true, "+1234".isPhone());
}
[TestMethod]
public void isPhone_ValidNumberPrefixedTwoPlus_false()
{
    Assert.AreEqual(false, "++1234".isPhone());
} 

10.isNumber

public static bool isNumber(this string input)
{
    var match = Regex.Match(input, @"^[0-9]+$", RegexOptions.IgnoreCase);
    return match.Success;
}  

And the test methods

Sorry, no test methods! task for you:) 

11.extractNumber

        public static int extractNumber(this string input)
{
    if (string.IsNullOrWhiteSpace(input)) return 0;

    var match = Regex.Match(input, "[0-9]+", RegexOptions.IgnoreCase);
    return match.Success ? match.Value.toInt() : 0;
} 

And the test methods

[TestMethod]
public void extractNumber_TextWith100_100()
{
    Assert.AreEqual(100, "in 100 between".extractNumber());
}

[TestMethod]
public void extractNumber_100_100()
{
    Assert.AreEqual(100, "100".extractNumber());
}

[TestMethod]
public void extractNumber_TextWithMixedNumbers_firstNumber()
{
    Assert.AreEqual(100, "first 100 then 60 then 8 then24".extractNumber());
} 

12.extractEmail

public static string extractEmail(this string input)
{
    if (input == null || string.IsNullOrWhiteSpace(input)) return string.Empty;

    var match = Regex.Match(input, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
    return match.Success ? match.Value : string.Empty;
}  

And the test methods

[TestMethod]
public void extractEmail_containsEmail_Email()
{
    Assert.AreEqual("email@email.com", "name,+86738238;email@email.com;address".extractEmail());
}

[TestMethod]
public void extractEmail_noEmail_empty()
{
    Assert.AreEqual("", "just some text".extractEmail());
}

[TestMethod]
public void extractEmail_Email_Email()
{
    Assert.AreEqual("abc@abc.com", "abc@abc.com".extractEmail());
}  

13.extractQueryStringParamValue

public static string extractQueryStringParamValue(this string queryString, string paramName)
{
    if (string.IsNullOrWhiteSpace(queryString) || string.IsNullOrWhiteSpace(paramName)) return string.Empty;

    var query = queryString.Replace("?", "");
    if (!query.Contains("=")) return string.Empty;
    var queryValues = query.Split('&').Select(piQ => piQ.Split('=')).ToDictionary(
      piKey => piKey[0].ToLower().Trim(), piValue => piValue[1]);
    string result;
    var found = queryValues.TryGetValue(paramName.ToLower().Trim(), out result);
    return found ? result : string.Empty;
}

And the test methods

[TestMethod]
public void extractQueryStringParamValue_twoParamsWantFirst_first()
{
    Assert.AreEqual("One", 
      "?param1=One¶m2=two".extractQueryStringParamValue("param1"));
}

[TestMethod]
public void extractQueryStringParamValue_invalidQueryString_empty()
{
    Assert.AreEqual("", "justfun".extractQueryStringParamValue("param1"));
}

Points of Interest

You can add/modify scenarios to the test methods to get desired output based on your requirement.For example in isPhone, you can put restriction on min and max length.Subsequently you change the extension method if the test method fails.

History

Initial version as on 5-Dec-2013

License

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

Share

About the Author

Prabu ram
Architect CGI
India India
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionVery Helpful Pin
Member 46626731-Aug-17 15:11
memberMember 46626731-Aug-17 15:11 
QuestionExtensions for VB Developers Pin
Member 130446837-Mar-17 12:02
memberMember 130446837-Mar-17 12:02 
QuestionNo Regex Pin
#realJSOP9-Sep-16 6:59
mve#realJSOP9-Sep-16 6:59 
SuggestionSee also in Namespace: System.Web.WebPages Pin
hs19768-Sep-16 23:14
memberhs19768-Sep-16 23:14 
QuestionVery Useful Pin
porkopek3-Jul-16 14:18
memberporkopek3-Jul-16 14:18 
SuggestionString.Reverse() Pin
Paul_Williams12-Dec-13 1:29
groupPaul_Williams12-Dec-13 1:29 
GeneralRe: String.Reverse() Pin
Prabu ram12-Dec-13 22:39
memberPrabu ram12-Dec-13 22:39 
GeneralRe: String.Reverse() Pin
Paul_Williams13-Dec-13 0:12
groupPaul_Williams13-Dec-13 0:12 
GeneralRe: String.Reverse() Pin
Prabu ram15-Dec-13 4:13
memberPrabu ram15-Dec-13 4:13 
SuggestionVery interessant Pin
SpArtA6-Dec-13 11:53
memberSpArtA6-Dec-13 11:53 
GeneralRe: Very interessant Pin
Prabu ram7-Dec-13 2:41
memberPrabu ram7-Dec-13 2:41 

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

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

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190518.1 | Last Updated 5 Dec 2013
Article Copyright 2013 by Prabu ram
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid