tags:

views:

3437

answers:

6

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).

+2  A: 

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;
   }
}
Dillie-O
Forgot that IndexOf will return -1 when the value doesn't exist. Thanks!
Metro Smurf
+14  A: 

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...

Arnshea
+1 for regex, since it's the right tool
Daniel LeCheminant
There's no explicit requirement that the paren-enclosed username must follow a name without spaces. "\(.*\)" should be sufficient.
James
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
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
A regular expression really is the right tool for the job in this case.
Ryan Duffield
The grouping should be within the escaped parenthesis, so you wouldn't have to strip the brackets afterwards; i.e. "\((.*)\)"
James
doh, you're right, the grouping should be within the escaped parens
Arnshea
Admittedly, I haven't really pursued RegEx's is mostly due not needing them very often and end up just forgetting what I learned.@James, et al - the initial RegEx of "\(.*\)" looks to express quite nicely (and simply) my intentions.I'll give this a go when I'm back at my dev machine.Cheers!
Metro Smurf
Trust me, take the time to learn Regular Expressions. They'll save the day (and give you added geek cred) when you least expect it. I know I gave a different answer, but that was for the sake of code cleanup 8^D
Dillie-O
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.
Metro Smurf
+6  A: 

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 );
}
paxdiablo
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.
Metro Smurf
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
+3  A: 

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().

Daniel Brückner
Nice use of the string API.
Mohit Chakraborty
This doesn't reflect the intention very well though.
ssg
First line: Kind of a hack ... ^^ ;)
Daniel Brückner
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.
Daniel Brückner
+1  A: 

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 :-) ...

MartinStettner
@Martin - Well, it is an (un)healthy aversion (-_^) Your comment about the substring being more expressive is definitely true.
Metro Smurf
+3  A: 

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.

Matthew Sposato