views:

377

answers:

6

I have a method that looks like this:

public static String escape(String text)
{
   String r = replace(text, "\\", "\\\\");
   r = replace(r, "\r", "\\r");
   r = replace(r, "\b", "\\b");
   r = replace(r, "\t", "\\t");
   r = replace(r, "\n", "\\n");
   r = replace(r, "\f", "\\f");
   return r;
}

Is there a faster, less brutal way to do this and if so what would that way look like?

Note that this is J2ME, so no Apache commons, and no regex.

+1  A: 

Once it works (comment noted above), the simplest way to speed it up is to perform the replacements using a StringBuffer.

But, have you measured this ? Is it a bottleneck ? I would suggest you do this before expending energy on optimisations.

Brian Agnew
+1  A: 

You probably want one of the methods from Apache Commons Lang StringEscapeUtils, such as unescapeJava()

skaffman
He's just edited the question to say it's J2ME :-(
Brian Agnew
Very sorry, but this site answers things so quickly that I hadn't time to fix my question to the one I'd actually meant to ask :)
izb
Absolutely. It's a blessing AND a curse :-)
Brian Agnew
**shakes fist**
skaffman
Still a relevant post. He could use the source if not the entire library.
Chris Kessel
A: 

hm can't you just use

public static String escape(String text)
{
   String r = replace(text, "\", "\\");
   return r;
}
iffi
The problem is that "\n" doesn't contain a '\' to find.
izb
I don't get it. Then the r = replace(r, "\n", "\\n"); is not right?
iffi
'\n' is a carriage return character; a single character. A '\' is a backslash; also a single character. I want to replace carriage returns with a backslash followed by a 'n'.
izb
@izb, why did you accept this answer? Fostah's answer is probably the best you're going to get, and you *know* this one's wrong.
Alan Moore
Whoops. Mis-clicked.
izb
+2  A: 

Do you mean "fast" for the developer or performance? If it's the latter, then use a char array & do it all yourself. It will be much faster since you can do it in one pass.

Wayne Young
an example would be great.
akf
Fast for the developer would be to keep the method I have. I was hoping for a fast-performing alternative that I could measure a difference with.
izb
A: 

Using the following would require traversing the characters in the String only once instead of x where x is the number of control characters to escape:

private static final Hashtable mapping = new Hashtable(6);

static {
    mapping.put(Character.valueOf('\\'), "\\\\");
    mapping.put(Character.valueOf('\r'), "\\r");
    mapping.put(Character.valueOf('\b'), "\\b");
    mapping.put(Character.valueOf('\t'), "\\t");
    mapping.put(Character.valueOf('\n'), "\\n");
    mapping.put(Character.valueOf('\f'), "\\f");
}

public static String escape(String text) {
    final StringBuffer escaped = new StringBuffer();

    for (int characterNumber = 0; characterNumber < text.length(); ++characterNumber) {
        final char current = text.charAt(characterNumber);
        if (mapping.containsKey(Character.valueOf(current))) {
            escaped.append(mapping.get(current));
        } else {
            escaped.append(current);
        }
    }
    return escaped.toString();
}
laz
Won't work with J2ME.
Fostah
I'm interested to learn why?
laz
J2ME doesn't support Generics or StringBuilder. It's loosely based on Java 1.3.
Fostah
Alright, generics and StringBuilder are now gone. Should that work for J2ME now?
laz
No, Map and HashMap are not part of J2ME either. You would need to use the Hashtable class.
Fostah
Done. I suppose from a J2ME perspective, this usefulness of this code depends on memory constraints versus having the mapping stored as a object. There are so few entries though, I'd imagine it wouldn't be too bad?
laz
And Hashtables are synchronized, just to add to the costs. The containsKey call could be replaced with '"\n\r\t\b\f\\\"\'".indexOf(current) != 1', I guess.
izb
+4  A: 

I would do something like below. You'll have to benchmark it for your own target devices to make sure, but I think it will certainly be faster since you are not creating a new String object for every replace. And, it will be better from a creating garbage on the heap standpoint, which may or may not help with heap fragmentation.

public static String escape(String text) { 
    if(text == null) {
        return null;
    }

    int numChars = text.length();
    char ch;
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < numChars; i++) {
        ch = text.charAt(i);

        switch(ch) {
            case '\\':  { sb.append("\\\\"); break; }
            case '\r':  { sb.append("\\r"); break; }
            case '\b':  { sb.append("\\b"); break; }
            case '\t':  { sb.append("\\t"); break; }
            case '\n':  { sb.append("\\n"); break; }
            case '\f':  { sb.append("\\f"); break; }
            default: {
                sb.append(ch);
                break;
            }
        }
    }
    return sb.toString();
}
Fostah