3

I've got problem to replace " to \" and \ to \\ and except " of json

test.txt input file

"a"     "b"     
{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"}
\uff08\u524d\u5bfe\u5fdc

I want to output like

\"a\"     \"b\"     
{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"}
\\uff08\\u524d\\u5bfe\\u5fdc
1
  • 1
    How complex is your file? Will the JSON string always be in a single line? Will the JSON string never contain { or }? Will there be multiple lines of data? Will the JSON data always be the only line(s) in the file that start with a {?
    – terdon
    Apr 9, 2019 at 11:34

3 Answers 3

5

To be more robust, you could do a full json parsing:

perl -0777 -pe '
  s@(".*?"|\\)|(\{(?:"(?:\\.|[^"])*+"|(?2)|[^"{}]++)*+\})|[^{}\\"]+@
  $1 ? $1 =~ s/["\\]/\\$&/gr : $&@gse'

Which on an input like

"a"     "b"     "c{d"
{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"}
{
  "1": {"x": "y"}
  "2": "}}}"
  "3": ["{\"x", "}"]
}
\uff08\u524d\u5bfe\u5fdc

gives

\"a\"     \"b\"     \"c{d\"
{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"}
{
  "1": {"x": "y"}
  "2": "}}}"
  "3": ["{\"x", "}"]
}
\\uff08\\u524d\\u5bfe\\u5fdc

You may want to clarify what you want to do if the input contains "foo\"bar" or "foo\nbar" outside of json objects.

2
  • how about all data in one line? like this "a","b",{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"},\uff08\u524d\u5bfe\u5fdc
    – JOIN MAX
    Apr 10, 2019 at 4:57
  • @JOINMAX, how about it? The code above should change it to \"a\",\"b\",{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"},\\uff08\\u524d\\u5bfe\\u5fdc, is it not what you want? Apr 10, 2019 at 21:37
3

In the simple example you show, this is easy. Just escape the characters only on lines that don't start with a {:

$ sed -E '/^[^{]/s|(["\])|\\\1|g' file 
\"a\"     \"b\"     
{"1":"female","2":"197312","3":"359","4":"201109","5":"mail"}
\\uff08\\u524d\\u5bfe\\u5fdc

However, things get considerably more complicated if your JSON can span several lines. For such cases, you can write a little script that counts the number of opening { and closing } and only applies the replacement while those numbers are equal (so when we aren't in a JSON string). Something like:

perl -F'' -ne 'for (@F){$op++ if /{/; $cl++ if /}/; if($cl==$op){s|["\\]|\\$&|g;}print}' file 

However, this will also break if the JSON string itself can contain { or } which don't signify a JSON section (e.g. {"1":"b-{c}"} or whatever). For such cases, use Stéphane's approach instead.

0
0
$ sed 's/\(\\\|^"\|"$\|"[ \t]\)/\\\1/g' test.txt | sed 's/\([ \t]\)"/\1\\"/g'
  • Replace \ or " at start of line or end of line or before space/tab character:
    's/\(\\\|^"\|"$\|"[ \t]\)/\\\1/g'
  • or " after space/tab character: sed 's/\([ \t]\)"/\1\\"/g'

So this only works if your json string doesn't contain any space/tab characters and is on one line.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .