views:

297

answers:

3

I want to change a String so that all the uppercase characters become lowercase, and all the lower case characters become uppercase. Number characters are just ignored.

so "AbCdE123" becomes "aBcDe123"

I guess there must be a way to iterate through the String and flip each character, or perhaps some regular expression that could do it.

+13  A: 

Apache Commons StringUtils has a swapCase method.

JacobM
Their StringUtils is one of the best tools for manipulating strings.
Trick
+1 for common sense
skaffman
Adding a dependency to a largish library for only a single function of very limited complexity could be argued against.
Daniel Schneller
Agreed about the dependency, but if you look around you'll probably find other places that Commons code can simplify your code.
JacobM
I'd also add that there are advantages to using an approach that is known to be heavily tested and widely used, such as Apache Commons. If you implemented it yourself, there might be nothing wrong with your basic algorithm, but (unless you get Jon Skeet to write it for you) you might have a little off-by-one error or some such that won't turn up until later.
JacobM
+1  A: 

I guess there must be a way to iterate through the String and flip each character

Correct. The java.lang.Character class provides you under each the isUpperCase() method for that. Test on it and make use of the toLowerCase() or toUpperCase() methods depending on the outcome. Append the outcome of each to a StringBuilder and you should be fine.

BalusC
No need to append to a StringBuilder - you already know exactly how big the output will be, and you can do it in an array in-place.
Jon Skeet
That's indeed doable if he don't need to manipulate it afterwards.
BalusC
Jon: If you honour locale-specific rules, changing the case of a string does not necessarily keep the length. E.g. the upper case of "ß" is "SS" for German locales.
jarnbjo
@jarnbjo: very spot on.
BalusC
+4  A: 

I don't believe there's anything built-in to do this (it's relatively unusual). This should do it though:

public static String reverseCase(String text)
{
    char[] chars = text.toCharArray();
    for (int i = 0; i < chars.length; i++)
    {
        char c = chars[i];
        if (Character.isUpperCase(c))
        {
            chars[i] = Character.toLowerCase(c);
        }
        else if (Character.isLowerCase(c))
        {
            chars[i] = Character.toUpperCase(c);
        }
    }
    return new String(chars);
}

Note that this doesn't do the locale-specific changing that String.toUpperCase/String.toLowerCase does. It also doesn't handle non-BMP characters.

Jon Skeet
This is almost precisely what the Apache Commons StringUtils code does, actually, except that they use charAt() to get each Character instead of converting to and from an array. Also, they check isTitleCase() as well (and convert to lower).
JacobM
@JacobM: How do they build the array in the end? I find that going to a char array, modifying in place and then building a String at the end is one of the more efficient ways of doing this sort of work.
Jon Skeet
Looks like they start by allocating a StringBuffer and appending each converted character to the buffer. I guess then the question is whether StringBuffer.toString is more or less efficient than new String(char[]). I couldn't tell you. Source code available at http://kickjava.com/src/org/apache/commons/lang/StringUtils.java.htm
JacobM
By using StringBuffer rather than StringBuilder they're already incurring the (small) synchronization penalty on every operation. Then there's the internal housekeeping for StringBuffer, keeping the length vs capacity etc. I'm not saying it's a big difference - but in both cases there'll be an extra (unavoidable) extra copy made, and I can't see any actual advantages to the StringBuffer approach.
Jon Skeet
@Sam: It may well depend on the version of Java you're using, but in the source code I'm looking at right now (1.6 I believe), StringBuffer.toString() uses the `public String(char[], int, int)` constructor which copies the array. (Any public constructor will have to copy the array, of course.)
Jon Skeet
This is quickly devolving into pedantry, at any rate it used to be the case that calling StringBuffer.toString() would reuse the char[] array by calling a package-private String constructor. I just checked, and that's no longer the case in 1.5+, StringBuilder and StringBuffer's toString methods copy the char array. At any rate, I like the char[] approach better.
Sam Barnum
Correct Jon, edited my comment.
Sam Barnum
Sam: String(Buffer|Builder).toString must have created a copy in earlier Java versions as well, as modifications to the StringBuffer cannot cause changes to previosuly created String instances.
jarnbjo
@jambjo: In 1.4, StringBuffer.toString() called new String(this) which referenced the original char[] array, and called setShared(true) on the StringBuffer. Any further modification to the "shared" StringBuffer caused the StringBuffer's char[] array to be copied, and unset the "shared" flag. This is no longer the case in 1.5+, maybe because it's more important to append things quickly to StringBuffers and StringBuilders without checking whether they're shared for every write operation.
Sam Barnum