5

C#, .NET 3.5

This just smells of ugly to me, but I can't think of another way.

Given a string with the format of "Joe Smith (jsmith)" (sans quotes), I'd like to parse out just the 'jsmith' string within the parenthesis. I've come up with this:

private static string DecipherUserName( string user )
{
    if( !user.Contains( "(" ) )
        return user;

    int start = user.IndexOf( "(" );

    return user.Substring( start ).Replace( "(", string.Empty ).Replace( ")", string.Empty );
}

Other than my (un)healthy aversion to RegEx, is there a simpler way to parse out the substring?

Edit: To clarify, the string to parse will always be of: "Joe Smith (jsmith)" (sans quotes).

6 Answers 6

20

Regexes are so useful that you'll save yourself a ton of heartache biting the bullet and learning them. Not the whole shebang, just the basics.

One regex that'll work is "\w+\((.*)\)" - jsmith would be in Match.Groups[1].

One easy way to pick up regexes is to find a website that'll let you type in a regex and some text then spit out the matches...

11
  • There's no explicit requirement that the paren-enclosed username must follow a name without spaces. "(.*)" should be sufficient.
    – James
    Commented Apr 8, 2009 at 0:02
  • Right tool but the wrong RE - you need one that will handle the case of a user without parentheses. Do that and I'll give you a vote :-)
    – paxdiablo
    Commented Apr 8, 2009 at 0:02
  • My experience with C# and regex, it's powerful as long as your string is below 10,000 chars or so. It gets dead slow after that, to the point where using replace and indexof are thousand times faster. But it's still the right tool for what you're doing below that number.
    – Lancelot
    Commented Apr 8, 2009 at 0:13
  • A regular expression really is the right tool for the job in this case. Commented Apr 8, 2009 at 0:13
  • 2
    "Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems." - jwz Commented Mar 5, 2012 at 15:05
9

You shouldn't need the first replace since you can just add 1 to the "(" position.

private static string DecipherUserName (string user) {           
    int start = user.IndexOf( "(" );
    if (start == -1)
        return user;
    return user.Substring (start+1).Replace( ")", string.Empty );
}
2
  • Of note: I accepted the refactor by Pax over the RegEx solution provided as I feel over the long run, this will be more maintainable until I become more versed in RegEx. That, and a quick test of 1000 names had the RegEx solution at .1165195 ms vs. String solution at .0077423 ms. Commented Apr 8, 2009 at 15:30
  • For a simple text match like this, REs are usually slower (although they can be compiled once for extra speed in a loop). Their real advantage comes with their expressiveness for more complex cases (e.g., allowing and removing spaces inside the parentheses). Very much worth learning, so keep it up.
    – paxdiablo
    Commented Apr 8, 2009 at 23:36
5

Kind of a hack ... ^^

return user.Substring(user.IndexOf('(') + 1).TrimEnd(')');

If user contains no opening parenthesis, IndexOf() returns -1, we add one, get zero, and SubString() returns the whole string. TrimEnd() will have no effect unless the user's name ends with a closing parenthesis.

If user contains a opening parenthesis, IndexOf() returns its index, we skipp the opening parenthesis by adding one, and extract the rest of the string with Substring(). Finally we remove the closing parenthesis with TrimEnd().

3
  • This doesn't reflect the intention very well though. Commented Apr 8, 2009 at 0:17
  • First line: Kind of a hack ... ^^ ;) Commented Apr 8, 2009 at 0:18
  • I really love regular expressions and would myself prefer a regex solution. But it is a nice oneliner and not more confusing than "\w+((.*))" to the uninformed. Commented Apr 8, 2009 at 0:22
5

If the user string is always in the form "Joe Smith (jsmith)", this should work.

private static string DecipherUserName(string user)
{
    string[] names = user.Split(new char[] {'(', ')'});
    return names.Length > 2 ? names[1] : user;
}

And if the user string is always "Joe Smith (jsmith)", this will always work.

private static string DecipherUserName(string user)
{
    return "jsmith";
}

The second entry for humor purposes only.

2

Since the IndexOf function will return -1 when the value doesn't exist, you could do things slightly different...

private static string DecipherUserName( string user )
{           
   int start = user.IndexOf( "(" );

   if (start > -1)
   {
      return user.Substring( start ).Replace( "(", string.Empty ).Replace( ")", string.Empty );
   }
   else
   {
      return user;
   }
}
1
  • Forgot that IndexOf will return -1 when the value doesn't exist. Thanks! Commented Apr 8, 2009 at 2:38
1

I'd use

int start=user.IndexOf('(');
if (start != -1) {
  end = user.IndexOf(')', start);
  return user.Substring(start+1,end-start-1);
} else
  return user;

But this is just a cosmetic change: using characters in IndexOf is a little bit faster, and using the Substring method seems to express more exactly what should be done (and the method is more robust if you have multiple pairs of parentheses...)

That said, Daniel L's method (using String.Split) might be simpler (but doesn't deal very well with malformed strings and has to construct a string array).

All in all, I'd suggest you to overcome your aversion to regular expressions, since that situation is exactly what they're intended for :-) ...

1
  • @Martin - Well, it is an (un)healthy aversion (-_^) Your comment about the substring being more expressive is definitely true. Commented Apr 8, 2009 at 2:37

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