Posts Tagged Extension Methods

C#: String Extension SplitOn to Split Text into Specific Sizes

This code string extension will take a specific number of words and create a list of strings up to that word boundary. For example if one split on this sentence: “The Fall River” would make three strings of “The “, “Fall “ and “River”.

I have placed this into a string extension which could be called such as

List<string> items = "The Fall River".SplitOn( 5 );

Here is the extension method

// using System.Text.RegularExpressions;
public static class StringExtensions
{

    /// <summary>Use this function like string.Split but instead of a character to split on,
    /// use a maximum line width size. This is similar to a Word Wrap where no words will be split.</summary>
    /// Note if the a word is longer than the maxcharactes it will be trimmed from the start.
    /// <param name="initial">The string to parse.</param>
    /// <param name="MaxCharacters">The maximum size.</param>
    /// <remarks>This function will remove some white space at the end of a line, but allow for a blank line.</remarks>
    ///
    /// <returns>An array of strings.</returns>
    public static List<string> SplitOn( this string initial, int MaxCharacters )
    {

        List<string> lines = new List<string>();

        if ( string.IsNullOrEmpty( initial ) == false )
        {
            string targetGroup = "Line";
            string theRegex = string.Format( @"(?<{0}>.{{1,{1}}})(?:\W|$)", targetGroup, MaxCharacters );

            MatchCollection matches = Regex.Matches( initial, theRegex, RegexOptions.IgnoreCase
                                                                      | RegexOptions.Multiline
                                                                      | RegexOptions.ExplicitCapture
                                                                      | RegexOptions.CultureInvariant
                                                                      | RegexOptions.Compiled );
            if ( matches != null )
                if ( matches.Count > 0 )
                    foreach ( Match m in matches )
                        lines.Add( m.Groups[targetGroup].Value );
        }

        return lines;
    }

}

Here is an example with output of its usage:

string text = "The rain in spain falls mainly on the plain of Jabberwocky falls.";

List<string> lines = text.SplitOn( 20 );

foreach ( string line in lines )
    Console.WriteLine( line );

/*
The rain in spain
falls mainly on the
plain of Jabberwocky
falls.
 */

foreach ( string line in text.SplitOn( 11 ) )
    Console.WriteLine( line );

/*
The rain in
spain falls
mainly on
the plain
of
Jabberwocky
falls.
 */ 
  • Share/Bookmark

Tags:

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/Bookmark

Tags: , ,

INI Files Meet Regex and Linq in C# to Avoid the WayBack Machine of Kernal32.Dll

BetweenStonesWhat if you are stuck having to deal with older technology such as INI files while using the latest and greatest C# and .Net there is available? This article discusses an alternate way to read INI files and extract the data from those dusty tomes while  easily accessing the resulting data from dictionaries. Once the data resides in the dictionaries we can easily extract the data using the power of the indexer on section name followed by key name within the section. Such as IniFile[“TargetSection”][“TargetKey”] which will return a string of the value of that key in the ini file for that section.

Note all the code is one easy code section at the bottom of the article so don’t feel you have to copy each sections code.

Overview

If you are reading this, chances are you know what INI files are and don’t need a refresher. You may have looked into using the Win32 Kern32.dll method GetPrivateProfileSection to achieve your goals. Ack!  “Set the Wayback machine Sherman!” Thanks but no thanks.

Here is how to do this operation using Regular Expressions (Kinda a way back machine but very useful) and Linq to Object to get the values into a dictionary format so we can write this line of code to access the data within the INI file:

string myValue = IniFile[“SectionName”][“KeyName”];

The Pattern

Let me explain the Regex Pattern. If you are not so inclined to understand the semantics of it skip to the next section.

string pattern = @"
^                           # Beginning of the line
((?:\[)                     # Section Start
 (?<Section>[^\]]*)         # Actual Section text into Section Group
 (?:\])                     # Section End then EOL/EOB
 (?:[\r\n]{0,}|\Z))         # Match but don't capture the CRLF or EOB
 (                          # Begin capture groups (Key Value Pairs)
   (?!\[)                    # Stop capture groups if a [ is found; new section
   (?<Key>[^=]*?)            # Any text before the =, matched few as possible
   (?:=)                     # Get the = now
   (?<Value>[^\r\n]*)        # Get everything that is not an Line Changes
   (?:[\r\n]{0,4})           # MBDC \r\n
  )+                        # End Capture groups";

Our goal is to use Named Match groups. Each match will have its section name in the named group called  “Section”  and all of the data, which is the key and value pairs will be named “Key” and “Value” respectively.  The trick to the above pattern is found in line eight. That stops the match when a new section is hit using the Match Invalidator (?!). Otherwise our key/values would bleed into the next section if not stopped.

The Data

Here is the data for your perusal.

string data = @"[WindowSettings]
Window X Pos=0
Window Y Pos=0
Window Maximized=false
Window Name=Jabberwocky

[Logging]
Directory=C:\Rosetta Stone\Logs
";

We are interested in “Window Name” and “Directory”.

The Linq

Ok, if you thought the regex pattern was complicated, the Linq to Objects has some tricks up its sleeve as well. Primarily since our pattern matches create a single match per section with the accompany key and value data in two separate named match capture collections, that presents a problem. We need to join the the capture collections together, but there is no direct way to do that for the join in Linq because that link is only an indirect by the collections index number.

How do we get the two collections to be joined?

Here is the code:

Dictionary<string, Dictionary<string, string>> InIFile
= ( from Match m in Regex.Matches( data, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline )
 select new
 {
  Section = m.Groups["Section"].Value,

  kvps = ( from cpKey in m.Groups["Key"].Captures.Cast<Capture>().Select( ( a, i ) => new { a.Value, i } )
     join cpValue in m.Groups["Value"].Captures.Cast<Capture>().Select( ( b, i ) => new { b.Value, i } ) on cpKey.i equals cpValue.i
     select new KeyValuePair<string, string>( cpKey.Value, cpValue.Value ) ).ToDictionary( kvp => kvp.Key, kvp => kvp.Value )

  } ).ToDictionary( itm => itm.Section, itm => itm.kvps );

Explanation:

  • Line 1: Our end goal object is a Dictionary where the key is the Section name and the value is a sub-dictionary with all the keys and values found in that section.
  • Line 2: The regex needs IPW because we have commented the pattern. It needs multiline because we are spanning multiple lines and need ^ to match each individual line and not just the beginning.
  • Line 5: This is the easiest item, simply access the named capture group “Section” for the section name.
  • Line 7 (.Captures) : Each one of the keys and values are in the specialized capture collection property off of the match.
  • Line 7 (.Cast<Capture>) : Since capture is specialized list and not a true generic list, such as List<string> we are going to Cast it(Cast<(Of <(TResult>) it (to IEnumerable<(Of <(T>)>),so we can access the standard query operators, i.e. the extension methods which are available to IEnumerable<T>. Short answer, so we can call .Select.
  • Line 7 (.Select): Because each list does not have a direct way to associate the data, we are going to create a new object that has a property which will have that index number, along with the target data value. That will allow us join it to the other list.
  • Line 7 (Lambda) : The lambda has two parameters, the first is our actual regex Capture object represented by a. The i is the index value which we need for the join. We then call new and create a new entity with two properties, the first is actual value of the Key found of the Capture class property “Value” and the second is i the index value.
  • Line 8 (Join) : We are going to join the data together using the direct properties of our new entity, but first we need to recreate the magic found in Line 7 for our Values capture collection. It is the same logic as the previous line so I will not delve into its explanation in detail.
  • Line 8 (on cpKey.i equals cpValue.i) : This is our association for the join on the new entities and yay, where index value i equals the other index value i allows us to do that. This is the keystone of all we are doing.
  • Line 9 (new KeyValuePair) : Ok we are now creating each individual linq projection item of the data as a KeyValuePair object. This could be removed for a new entity, but I choose to use the KeyValuePair class.
  • Line 9 (ToDictionary) : We want to easily access these key value pairs in the future, so we are going to place the Key into a Key of a dictionary and the dictionary key’s value from the actual Value.
  • Line 11 (ToDictionary) : Here is where we take the projection of the previous lines of code and create the end goal dictionary where the key name is the section and the value is the sub dictionary created in Line 9.

Whew…what is the result?

Console.WriteLine( InIFile["WindowSettings"]["Window Name"] ); // Jabberwocky
Console.WriteLine( InIFile["Logging"]["Directory"] );          // C:\Rosetta Stone\Logs

Summary

Thanks to the power of regular expressions and Linq we don’t have to use the old methods to extract and process the data. We can easily access the information using the newer structures. Hope this helps and that you may have learned something new from something old.

Code All in One Place

Here is all the code so you don’t have to copy it from each section above. Don’t forget to include the using System.Text.RegularExpressions to do it all.

string data = @"[WindowSettings]
Window X Pos=0
Window Y Pos=0
Window Maximized=false
Window Name=Jabberwocky

[Logging]
Directory=C:\Rosetta Stone\Logs
";
string pattern = @"
^                           # Beginning of the line
((?:\[)                     # Section Start
     (?<Section>[^\]]*)     # Actual Section text into Section Group
 (?:\])                     # Section End then EOL/EOB
 (?:[\r\n]{0,}|\Z))         # Match but don't capture the CRLF or EOB
 (                          # Begin capture groups (Key Value Pairs)
  (?!\[)                    # Stop capture groups if a [ is found; new section
  (?<Key>[^=]*?)            # Any text before the =, matched few as possible
  (?:=)                     # Get the = now
  (?<Value>[^\r\n]*)        # Get everything that is not an Line Changes
  (?:[\r\n]{0,4})           # MBDC \r\n
  )+                        # End Capture groups";

Dictionary<string, Dictionary<string, string>> InIFile
= ( from Match m in Regex.Matches( data, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline )
    select new
    {
        Section = m.Groups["Section"].Value,

        kvps = ( from cpKey in m.Groups["Key"].Captures.Cast<Capture>().Select( ( a, i ) => new { a.Value, i } )
                 join cpValue in m.Groups["Value"].Captures.Cast<Capture>().Select( ( b, i ) => new { b.Value, i } ) on cpKey.i equals cpValue.i
                 select new KeyValuePair<string, string>( cpKey.Value, cpValue.Value ) ).ToDictionary( kvp => kvp.Key, kvp => kvp.Value )

    } ).ToDictionary( itm => itm.Section, itm => itm.kvps );

Console.WriteLine( InIFile["WindowSettings"]["Window Name"] ); // Jabberwocky
Console.WriteLine( InIFile["Logging"]["Directory"] );          // C:\Rosetta Stone\Logs
  • Share/Bookmark

Tags: , ,

C# 101: Extension Method Creation For Variables and Enums

Class101 With the advent of C# 3.0 in .Net 3.5 one can create extension methods for common tasks for any existing types and enums which one finds in their code. This is a quick how-to article which provides those two examples using an integer and a user defined enum.

Basic Extension Example

Sometimes one needs to know when an integer value is negative. Here is an extension method to do that and its usage example.

public static class MyExtensions
{
    public static bool IsNegative( this int value )
    {
        return (value < 0) ? true : false;
    }
}
  1. Step one create a static class.
  2. Create a static method return type can vary to suit the target operations needs.
  3. Use the this keyword in the input parameters of the type which the extension will be used by.

In the above case we want integers to report if they are negative or not. Here is the usage:

int value = 1;

Console.WriteLine( value + " is " + value.IsNegative() );
value = --value;

Console.WriteLine( value + " is " + value.IsNegative() );

value = --value;
Console.WriteLine( value + " is " + value.IsNegative() );

/* Output
1 is False
0 is False
-1 is True
*/

Enum Example

Enums can have extensions in C# as well and are very functional.

This example is similar to the previous but we will deal with software development states. The first two states are test related and we will create an extension to determine if the state is in test or not.

public enum States { Test, UserAcceptance, Development, Production, Maintainence };

public static class MyExtensions
{
    public static bool IsInTest( this States state )
    {
        return state < States.LastTestState;
    }

}

So every state before Development is considered a “Testing” state. Here is its usage:

States current = States.Test;
Console.WriteLine("State: " + current + " is " + current.IsInTest());

current = States.Development;
Console.WriteLine( "State: " + current + " is " + current.IsInTest() );

/* Output:
State: Test is True
State: Development is False
*/

HTH

HTH

  • Share/Bookmark

Tags: , , ,

C#: Splitting Data From a String and Extracting out Decimals and Integers into Separate Lists Using Extension Methods

Sometimes you have the need to extract text out of a string, once extracted you need to get the right data into lists. Say one list of integers and one list of decimals. Using the extension methods now found in .Net one can easily do those operations.

Example

Here is the data in a string, “12 34 56.1 18 19.25 41”. We want the integers separated out from the decimals into their own lists respectively as their native values (int/decimal) and not strings. Using Where, Select and a couple of Lambdas we can get to the intended  result.

string data =@"12 34 56.1 18 19.25 41";

string[] dataAsStrings = data.Split( ' ' );

List<decimal> decs = ( dataAsStrings.Where( itm => itm.Contains( '.' ) ) )
                  .Select( itm => Decimal.Parse( itm ) )
                  .ToList();

List<int> ints = ( dataAsStrings.Where( itm => itm.Contains( '.' ) == false ) ).Select( itm => int.Parse( itm ) ).ToList();

foreach ( decimal dc in decs )
    Console.WriteLine( dc );
/* Outputs 56.1 19.25 (on seperate lines)*/

foreach ( int it in ints)
    Console.WriteLine( it );
/* Outputs 12 34 18 41 (on seperate lines)*/
  • Step one is top split the string using string.Split.
  • Step 2 is take that split data and enumerate over it using the Where method.
  • Within the Where we use a lambda which basically says for each item in the list, if it contains a period select it and return it. Note the result of most Extension methods is deemed a projection which is really a list of the values. Where, Select and ToList all return projections.
  • Once the Where is done we call/chain the Select Extension. The select is saying take the projection from the Where and on each item parse out the value and return it in its native, non string format.
  • Using the ToList on the projection created by the Select we take the items and specify it to be in a List<> format.

We didn’t have to do the ToList and could have said something like this:

var decs = ( dataAsStrings.Where( itm => itm.Contains( '.' ) ) )
        .Select( itm => Decimal.Parse( itm ) );
foreach ( decimal dc in decs )
    Console.WriteLine( dc );
/* Outputs 56.1 19.25 (on seperate lines)*/

Which would have been just as vaild. At runtime the var would have been internally known as IEnumerable<decimal> under the covers and as you can see that is just a different type of list and works just the same.

  • Share/Bookmark

Tags: , , , , , ,

OmegaMan's Musings is Digg proof thanks to caching by WP Super Cache