Archive for the ‘Code Utilities’ Category.

C#: Handling Title Case in Strings with Articles and Prepositions

iStock_000002240961XSmall

This was an issue I answered in the forums which a user presented and felt that the response was intricate enough to share with the world as a whole. The user wanted to have a string converted to title case but also wanted to have the first letter of any article or preposition to not be be upper case along with the rest of the sentence. This article discusses how to do that in C#.

For example the user was interested in changing

“ALL QUIET ON THE WESTERN FRONT”

   to

“All Quiet on the Western Front.”

.Net Framework Almost Does It

Thanks to the TextInfo class and a helping hint from a current CultureInfo object we can use the method ToTitleCase to work with our current language. The problem is that when ToTitleCase is called with the original sentence we get this:

“All Quiet On The Western Front”

Give it some Help

The .Net code is not robust enough to ignore the articles and prepositions so we will augment it. The following code using Linq-to-Object and Regex and processes majority of the target articles and prepositions . I have placed it into an extension method below:

/*
using System.Globalization;
using System.Threading;
using System.Text.RegularExpressions;
*/

/// <summary>
/// An Extension Method to allow us t odo "The Title Of It".asTitleCase()
/// which would return a TitleCased string.
/// </summary>
/// <param name="title">Title to work with.</param>
/// <returns>Output title as TitleCase</returns>
public static string asTitleCase ( this string title)
{
    string WorkingTitle = title;

    if ( string.IsNullOrEmpty( WorkingTitle ) == false )
    {
        char[] space = new char[] { ' ' };

        List<string> artsAndPreps = new List<string>()
            { "a", "an", "and", "any", "at", "from", "into", "of", "on", "or", "some", "the", "to", };

        //Get the culture property of the thread.
        CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
        //Create TextInfo object.
        TextInfo textInfo = cultureInfo.TextInfo;

        //Convert to title case.
        WorkingTitle = textInfo.ToTitleCase( title.ToLower() );

        List<string> tokens = WorkingTitle.Split( space, StringSplitOptions.RemoveEmptyEntries ).ToList();

        WorkingTitle = tokens[0];

        tokens.RemoveAt(0);

        WorkingTitle += tokens.Aggregate<String, String>( String.Empty, ( String prev, String input )
                                => prev +
                                    ( artsAndPreps.Contains( input.ToLower() ) // If True
                                        ? " " + input.ToLower()              // Return the prep/art lowercase
                                        : " " + input ) );                   // Otherwise return the valid word.

        // Handle an "Out Of" but not in the start of the sentance
        WorkingTitle = Regex.Replace( WorkingTitle, @"(?!^Out)(Out\s+Of)", "out of" );
    }

    return WorkingTitle;

}
Explanation
  • Line 21: Here is our English list of words not to capitalize. We would have to change this for other languages.
  • Line 25: We get the current culture from the running thread so that ToTitleCase can do its job.
  • Line 30: ToTitleCase does the first run and upper cases all the first letters and drops any following upper case letters if they exist.
  • Line 32: We split the line on space between the words into word tokens and put them in a list.
  • Line 34: We save off the first word because regardless of what it is, it is correct.
  • Line 36: We remove the first word so not to process it.
  • Line 40: Using the Aggregate extension to accumulate each word token we will add a space. We are using the aggregate method in-lieu of string.Join to add spaces to our words (the accumulation), but also to check each word as it goes by which string.Join can’t help us with.
  • Line 42: As the tokens (words) are handed to us, check to see if they are in the list we setup in line 21. If it exists, add a space in front and make the whole word lower case (Line 43) other wise ad a space and just return the word.
  • Line 46: Handle any two word Out Of issues, but ignore if it is the first word as found in “Out of Africa”.
Tests and Results

 

Console.WriteLine( "ALL QUIET ON THE WESTERN FRONT".asTitleCase() );
Console.WriteLine( "Bonfire OF THE Vanities".asTitleCase() );
Console.WriteLine( "The Out-of-Sync Child: Recognizing and Coping with Sensory Processing Disorder".asTitleCase() );
Console.WriteLine( "Out OF AFRICA".asTitleCase() );

/* Results
All Quiet on the Western Front
Bonfire of the Vanities
The Out-Of-Sync Child: Recognizing and Coping With Sensory Processing Disorder
Out of Africa
*/
Share

C#: Generate a Random Sequence of Numbers and Letters From a User Defined Pattern and Characters

Here is a quick little snippet of code which uses allows one to create a sequence of numbers and letters, or any character needed, in a sequence defined by the user. Here is the code

Random rn = new Random();
string charsToUse = "AzByCxDwEvFuGtHsIrJqKpLoMnNmOlPkQjRiShTgUfVeWdXcYbZa1234567890";

MatchEvaluator RandomChar = delegate (Match m)
{
    return charsToUse[rn.Next( charsToUse.Length )].ToString();
};

Console.WriteLine( Regex.Replace( "XXXX-XXXX-XXXX-XXXX-XXXX", "X", RandomChar ) );
// Lv2U-jHsa-TUep-NqKa-jlBx
Console.WriteLine( Regex.Replace( "XXXX", "X", RandomChar ) );
 // 8cPD

What is happening is that in the Regex.Replace we specify a pattern by X’s. Anything which is not an X is ignored and left in the result. We specify what characters to use in the string charsToUse and in this case we have A-Za-z0-9 expressed as written out. When we run the Regex.Replace it returns the pattern we specified with the characters we needed.

Share

C#: Does the String Have Content – My Newest Favorite Extension Method

Everyone knows that one should do two things before using a string in C#. One is to check to see if it is null and the other is to check to see if it actually has content, not just blank. The primary way of doing that is to use the static method on the stringIsNullOrEmpty such as

string txt;

if (string.IsNullOrEmpty( txt ) == false)
{
   // Valid string do something
}

I have religiously been using that string checking paradigm (see my 2007 rants on it here (Is String.IsNullOrEmpty Hazardous to your Code’s health in .Net 2.0/3.0?)) for awhile now.

Issues

But I had two issues with using string.IsNullOrEmpty:

  1. The negative result of the method call (false) is telling me that the string is OK to use. Every so often I would call it without checking for that false condition. Grrr! I only want to do actions on it if it valid, not invalid.
  2. My way of working/thinking is that I type in the string name first and then think of testing it. Visual Studio’s Intellisense doesn’t help me, for I have already typed in the variable. I have to go back and add string.IsNullOrEmpty.

Solution

With the advent of extension methods I can solve both issues by making an extension method of what I want. By giving it a name which suggests a positive result means that the string is viable, I solve #1. By making it an extension method, I solve #2 above because it now shows up in Intellisense! Here is the code written as an extension

/// <summary>
/// Does the string contains text? Same as (IsNullOrEmpty() == false).
/// </summary>
/// <param name="txt">Text to look at.</param>
/// <returns>True if valid and contains text.</returns>
public static bool ContainsText( this string txt )
{
    return string.IsNullOrEmpty( txt ) == false;
}

Because I name it with a C, it shows up in the first set of selections in intellisense. That is a bonus.

Note this works even if the string is null! Give it a try!

Share

C#: Exception Handling Quick Read

From postings on the MSDN Forums and my own work, I have a template that allows for a quick read of an exception in C#. The following code will format the exception so it can be read on either the console or in a message box.

public static string Create(Exception ex)
{
 return string.Format(
   "Exception:\t{1}{0}Message:\t{2}{0}Source:\t\t{3}{0}TargetSite:\t{4}{0}{0}StackTrace{0}--------------{0}{0}{5}{0}",
   System.Environment.NewLine,
   ex.GetType(),
   ex.Message.Replace("\r", "").Replace('\n', '\t'),
   ex.Source,
   ex.TargetSite.ToString(),
   ex.StackTrace);
}

Here is what it look likes if you pipe it to a message box as in MessageBox.Show( MyClass.Create ( ex ) )

Message Box View of the Method's output.

As one can see all the important information is nicely spaced out and readable.

Share