29 October 2012

C# extension - Parse to decimal regardless of regional settings

A problem that often comes up is that decimal numbers are wrongly parsed on different computers due to the regional settings. The following extensions are intended to ensure that a string is correctly parsed to a decimal, regardless of the decimal separator used by the system.

Parse

The first extension returns a string that can be correctly parsed to a decimal type.
/// <summary>
/// Parses a string that is supposed to be a decimal number in
/// such a way that it can be parsed to a decimal data type.
/// </summary>
/// <param name="source">The string to be parsed.</param>
/// <returns>A string that can be parsed to a decimal data type.</returns>
public static string ParseDecimalString(this string source) {
  if (!source.HasValue()) {
    return "0";
  }
  else {
    int indexLastSep = source.LastIndexOfAny(new char[] { '.', ',' });
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < source.Length; i++) {
      if (char.IsDigit(source[i]))
        result.Append(source[i]);
      else if (i == indexLastSep)
        result.Append(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
      else if (i == 0 && source[i] == '-')
        result.Append('-');
    }
    return result.ToString();
  }
}

Cast

The second extension does the casting. I separated this one from the parsing for reusability.
/// <summary>
/// Converts from string to decimal. Returns 0m on failure.
/// </summary>
/// <param name="source">The string.</param>
/// <returns>A decimal representing the string or 0m on failure.</returns>
public static decimal ToDecimal(this string source) {
  decimal result = 0m;
  decimal.TryParse(source.ParseDecimalString(), out result);
  return result;
}

25 October 2012

Extension - Strip a string

One day I wrote this convienient extension that removes letters, digits and/or symbols - as specified - from a string:
/// <summary>Removes characters of specified types.</summary>
/// <param name="source">The string to remove from.</param>
/// <param name="removeTypes">A string containing 'L' to remove letters, 'D' to remove digits and 'S' to remove symbols.</param>
/// <returns>A string containing the remaining characters.</returns>
public static string Strip(this string source, string removeTypes) {
  bool letters = !string.IsNullOrEmpty(removeTypes) && removeTypes.Contains('L');
  bool digits = !string.IsNullOrEmpty(removeTypes) && removeTypes.Contains('D');
  bool symbols = !string.IsNullOrEmpty(removeTypes) && removeTypes.Contains('S');

  StringBuilder result = new StringBuilder();
  if (!string.IsNullOrEmpty(source)) {
    foreach (char c in source) {
      if (char.IsLetter(c)) {
        if (!letters) result.Append(c);
      }
      else if (char.IsDigit(c)) {
        if (!digits) result.Append(c);
      }
      else {
        if (!symbols) result.Append(c);
      }
    }
  }
  return result.ToString();
}
Note that the line removeTypes.Contains(char) relies on System.Linq.