7
\$\begingroup\$

I need a method that clean up a string considering these few rules:

  • Remove or not Spaces
  • Remove or not Special Chars
  • Remove or not Numbers
  • Descaptalize or not the string (this is don't really needed but I included it anyway).

I did some google if exists something that already do this, but found nothing, so I came up with this:

public static class StringHelper
{
    private static readonly Dictionary<char, char> _specialChars
        = new Dictionary<char, char>
    {
        { 'á', 'a' },{ 'à', 'a' },{ 'â', 'a' },{ 'ã', 'a' },{ 'ä', 'a' },
        { 'é', 'e' },{ 'è', 'e' },{ 'ê', 'e' },{ 'ë', 'e' },
        { 'í', 'i' },{ 'ì', 'i' },{ 'î', 'i' },{ 'ï', 'i' },
        { 'ó', 'o' },{ 'ò', 'o' },{ 'õ', 'o' },{ 'ö', 'o' },
        { 'ú', 'u' },{ 'ù', 'u' },{ 'û', 'u' },{ 'ü', 'u' },
        { 'ç', 'c' },{ 'ñ', 'n' }
    };

    static StringHelper()
    {
        foreach (var item in _specialChars.ToList())
        {
            _specialChars.Add(
                item.Key.ToString().ToUpper()[0],
                item.Value.ToString().ToUpper()[0]);
        }
    }

    private static readonly Regex _spaceRegex = new Regex(@"\s", RegexOptions.Compiled);
    private static readonly Regex _numericRegex = new Regex(@"\s", RegexOptions.Compiled);

    public static string Normalize(this string str, CharType normaliar = CharType.All)
    {
        if (normaliar.HasFlag(CharType.Spaces))
        {
            str = _spaceRegex.Replace(str, string.Empty);
        }

        if (normaliar.HasFlag(CharType.Special))
        {
            str = _specialChars.ToList()
                .Aggregate(str, (ac, item) => ac.Replace(item.Key, item.Value));
        }

        if (normaliar.HasFlag(CharType.Numerics))
        {
            str = _numericRegex.Replace(str, string.Empty);
        }

        if (normaliar.HasFlag(CharType.UpperCase))
        {
            str = str.ToLower();
        }

        return str;
    }
}

public enum CharType
{
    Spaces = 1,
    Special = 2,
    Numerics = 4,
    UpperCase = 8,
    All = 15,
}

I'm not considering really all special char just Portuguese, Spanish and a few more.

As far as I could, I wrote with the better performance and organization, but this was my limit.

\$\endgroup\$
4
\$\begingroup\$
private static readonly Regex _numericRegex = new Regex(@"\s", RegexOptions.Compiled);

That should be \d, not \s.


Instead of

_specialChars.Add(
    item.Key.ToString().ToUpper()[0],
    item.Value.ToString().ToUpper()[0]);

You can write

_specialChars.Add(
    Char.ToUpperInvariant(item.Key),
    Char.ToUpperInvariant(item.Value));

I find it a bit cleaner, and there is less work involved.


If the text is potentially very long, you may be better off doing just one pass of the string. You would need to profile against the sort of data you have, but it could look something like this

var spaces = normaliar.HasFlag(CharType.Spaces);
var numerics = normaliar.HasFlag(CharType.Numerics);
var uppercase = normaliar.HasFlag(CharType.UpperCase);
var special = normaliar.HasFlag(CharType.Special);

var sb = new StringBuilder(str.Length);

for (var i = 0; i < str.Length; i++)
{
    var c = str[i];
    if (spaces && Char.IsWhiteSpace(c))
    {
        continue;
    }

    if (numerics && Char.IsNumber(c))
    {
        continue;
    }

    if (uppercase)
    {
        c = Char.ToLowerInvariant(c);
    }

    if (special)
    {
        char replacement;
        if (_specialChars.TryGetValue(c, out replacement))
        {
            c = replacement;
        }
    }

    sb.Append(c);
}

return sb.ToString();
|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ "That should be \d, not \s." -- Why? \$\endgroup\$ – RubberDuck Jan 30 '15 at 0:11
  • \$\begingroup\$ @RubberDuck \s matches any whitespace character, \d matches any decimal digit. The way it is, no digits are removed -- "123".Normalize(CharType.Numerics) returns "123", when it should return the empty string. \$\endgroup\$ – mjolka Jan 30 '15 at 0:19
  • \$\begingroup\$ Lol. Yeah. I know. I was just hinting that you might want to add that to your answer is all. =;)- \$\endgroup\$ – RubberDuck Jan 30 '15 at 0:32
  • \$\begingroup\$ in the last if, when you execute _specialChars.TryGetValue(c, out replacement) if the char is not a special char, the line c = replacement will replace the char for a defaut(char) I'm right? Is need a if like: if(c != default(char)) c = replacement; right? \$\endgroup\$ – Jonny Piazzi Jan 30 '15 at 14:28
  • \$\begingroup\$ I put in test to compare the performance of Regex/String replace (original) vs StringBuilder(this answer) and, for my surprise, and for my surprise the first is around 8 times faster. \$\endgroup\$ – Jonny Piazzi Jan 30 '15 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.