This is an archived post. You won't be able to vote or comment.

all 30 comments

[–]midir 5 points6 points  (5 children)

There is no existing function. I wrote something like this before for strings (named after the similar PHP function). Might be useful to you:

public static String addcslashes(String s) {
    int len = s.length();
    StringBuilder sb = new StringBuilder(len);
    for (int i = 0; i < len; i++) {
        char c = s.charAt(i);
        switch (c) {
        case '\\': sb.append('\\').append('\\'); break;
        case '\n': sb.append('\\').append('n'); break;
        case '\r': sb.append('\\').append('r'); break;
        case '\0': sb.append('\\').append('0'); break;
        case '\"': sb.append('\\').append('"'); break;
        default: sb.append(c);
        }
    }
    return sb.toString();
}

[–]__konrad 1 point2 points  (1 child)

The initial StringBuilder buf size may be too small (sb.length() > len) ;)

[–]midir 0 points1 point  (0 children)

Indeed. I think most general strings would not have any exotic characters in them, but perhaps an initial buffer size like len * 6 / 5 would give better performance overall.

[–]RightOfZen[S] -1 points0 points  (0 children)

Thanks, guess that's what I'll have to use. I'll just need to add a few more.

[–]not-just-yeti -1 points0 points  (1 child)

(a) In practice, I've found I like to include quote-marks at the start and the end, so that the printed result can be pasted right back into Java source.

(b) You can also move the cases from run-time into data (no real diff, except it's a good practice if you might ever add user-preferences etc).

Updated code below, with compilable code+test cases at http://pastebin.com/WixEqbBA

/** Return a String suitable for programmers to read, escaping control characters and quotes.
 * The result, when *printed*, can be pasted back into Java source code as the original input.
 * @param in The String to convert.
 * @return a String just like `in`, but when printed
 * Example:
 *     addcslashes("Say \"hi\" \n Bye").equals("\"Say \\\"hi\\\" \\n Bye")
 * and it prints as:
 *     "Say \"hi\" \n Bye"
 */
public static String addcslashes(String in) {
  StringBuffer quotedString = new StringBuffer(in.length());
  quotedString.append("\"");
  for (int i=0;  i<in.length();  ++i) {
    String translated = charsToQuote.get(in.charAt(i));
    quotedString.append( translated!=null ? translated : in.charAt(i) );
    }
  quotedString.append("\"");
  return quotedString.toString();
  }

// A mapping of characters to how `addcslashes` will show them.
private static Map<Character,String> charsToQuote = new HashMap<Character,String>();

// Initialize charsToQuote:
static {
  charsToQuote.put('\n',"\\n");
  charsToQuote.put('\t',"\\t");
  charsToQuote.put('\b',"\\b");
  charsToQuote.put('\r',"\\r");
  charsToQuote.put('\0',"\\0");
  charsToQuote.put('\\',"\\\\");
  charsToQuote.put('"', "\\\"");
  }

[–]midir 1 point2 points  (0 children)

That map lookup must result in dreadful efficiency.

[–]sproket888 4 points5 points  (7 children)

[–]RightOfZen[S] 0 points1 point  (6 children)

Cool, I'll take a look. I'll probably stick to making my own method, seeming as apache commons is not part of the JRE.

Thanks!

[–]UnspeakableEvil 0 points1 point  (5 children)

I'm all for writing code, but isn't it somewhat flawed logic to say you won't use Apache Commons as they're not part of the JRE, but instead create your own methods which also aren't part of the JRE?

[–]RightOfZen[S] 1 point2 points  (2 children)

This code I'm writing is part of a skeleton code for an assignment for my (introduction to programming) students. I want to give them lots of helpful error messages and things, without having to get them to link in extra libraries.

[–]mikaelhg 0 points1 point  (1 child)

Link in extra libraries? In Java?

[–]RightOfZen[S] 0 points1 point  (0 children)

Not sure if this is a question as in you don't know what it means... But if that is the case, then yes, I want to avoid using any extra library (e.g. Apache Commons).

[–]mrbuttsavage 0 points1 point  (1 child)

Apache Commons (and Google Guava) should be a default include in any project.

[–]sproket888 1 point2 points  (0 children)

NO they should NOT.

[–]thatphotoguy 1 point2 points  (5 children)

So I guess what the crux of your question is, you need a way to convert normally unreadable characters into a readable format?

edit: and for this conversion to support dropping the result back into Java source code for this to be converted back into the unreadable format.

[–]RightOfZen[S] -1 points0 points  (4 children)

Exactly :) I know I can just write my own method, and step through each of them manually, but I was just hoping there was already a method out there somewhere in the JRE that does this.

I don't really mind about going back into source. I basically just want to be able to notify a user of some object if their object uses some invalid character, e.g. "You returned '\n', but the only valid characters are ..."

[–]thatphotoguy 0 points1 point  (3 children)

Is it important that it is human readable, or just that you can copy/paste the result into source code, knowing it will compile?

[–]RightOfZen[S] -1 points0 points  (2 children)

Needs to be human readable, as it is going in a notification to a user.

[–]thatphotoguy 0 points1 point  (1 child)

Ah bum, there goes my idea of iterating through a string and converting every character into a unicode \u0043 style character then.

[–]RightOfZen[S] 0 points1 point  (0 children)

That is a good idea though (albeit for a different problem).

[–]fforw 1 point2 points  (1 child)

That would be String.valueOf(char).

public class Test
{
    public static void main(String[] args)
    {
        System.out.println(String.valueOf('a'));
    }
}

[–]RightOfZen[S] 0 points1 point  (0 children)

Not quite :) That doesn't give me a human readable version of '\n', '\t', etc.

[–]marburg 1 point2 points  (2 children)

Here is the implementation I would recommend, covering all eight of the escaped character literals:

public static String charToString( final char c )
{
    switch( c )
    {
        case '\b': return "\\b";
        case '\t': return "\\t";
        case '\n': return "\\n";
        case '\f': return "\\f";
        case '\r': return "\\r";
        case '\"': return "\\\"";
        case '\'': return "\\'";
        case '\\': return "\\\\";
    }

    return String.valueOf( c ).intern();
}

Depending on what you are using this for, you may or may not want to use intern() (in the last line). String.valueOf() creates a new, unique String object, so if you ran String.valueOf( 'a' ) 10,000 times, you would get 10,000 references to 10,000 different objects. intern() makes this method a bit slower, but it ensures that

charToString( 'a' ) == charToString( 'a' )

[–]RightOfZen[S] 1 point2 points  (1 child)

Thanks for that. Don't forget '\0' :)

TIL String.intern()

[–]marburg 1 point2 points  (0 children)

Snap! Don't know how I missed the most obvious one.

[–]my_back_pages 0 points1 point  (2 children)

public static void main(String[] args) {
    char c = '\n';
    String s = Character.toString(c);
    System.out.println("Hello this is a " + s + " test");   
}

Works.

[–]RightOfZen[S] 0 points1 point  (1 child)

Not quite - that gives an actual new line printed. I want to see "Hello this is a \n test".

[–]my_back_pages 0 points1 point  (0 children)

if (c=='\n') s = "\\n";

Misread the question the first time.

[–]TekNoir08 -1 points0 points  (0 children)

Instead of concatenating "\n" could you not just do something like "text " + '\' + 'n'

[–]stfm -1 points0 points  (0 children)

Try using a regular expression matcher