Automatic conversion from string concatenation to formatted arguments

Our code is littered with things like

Log.d("Hello there " + x + ", I see your are " + y + " years old!");

      

I want a script to convert to something like this,

Log.d("Hello there %s, I see your are %d years old!", x, y);

      

(Note: I'm not worried about getting the correct argument type now. I could preprocess the file to determine the types, or convert to always use strings. Not my concern right now.)

I am wondering if anyone has resolved this. I came up with these regex for pulling static and variable parts of strings,

static final Pattern P1 = Pattern.compile("\\s*(\".*?\")\\s*");
static final Pattern P2 = Pattern.compile("\\s*\\+?\\s*([^\\+]+)\\s*\\+?\\s*");

      

After completing the cycle find()

for each, I can pull out the pieces,

  • "Hi there"
  • ", I see that you are"
  • "years old!"

and

  • x
  • at

But I can't think of a good way to put them together, given all the possibilities of how they can be put together.

Maybe this is the wrong approach and I should try to pull it out and then replace the variable with a format argument?

Any ideas? thank.

+3


source to share


2 answers


You can use the following regex to capture all arguments and lines in one go. Therefore, you can determine exactly where the arguments should fit into the overall string using pairs.

(?:(\w+)\s*\+\s*)?"((?:[^"\\]|\\.)*+)"(?:\s*\+\s*(\w+))?


Demo of Regex is here . (Thanks to nhahtdh for the improved version.)

It will find all concatenations as part of Log.d in the format:
[<variable> +] <string> [+ <variable>]


Where []

denotes an optional part.

With this, you can form appropriate replacements, do the following example:



import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.lang.StringBuilder;
import java.util.List;
import java.util.ArrayList;

class Main {
  public static void main(String[] args) {
    String log = "Log.d(\"Hello there \" + x + \", I see your are \" + y + \" years old!\");";
    System.out.println("Input: " + log);
    Pattern p = Pattern.compile("(?:(\\w+)\\s*\\+\\s*)?\"((?:[^\"\\\\]|\\\\.)*+)\"(?:\\s*\\+\\s*(\\w+))?");
    Matcher m = p.matcher(log);
    StringBuilder output = new StringBuilder(25);
    List<String> arguments = new ArrayList<String>(5);
    output.append("Log.d(\"");
    while (m.find()) {
        if (m.group(1) != null) {
            output.append("%s");
            arguments.add(m.group(1));
        }
        output.append(m.group(2));
        if (m.group(3) != null) {
            output.append("%s");
            arguments.add(m.group(3));
        }
    }
    output.append("\"");
    for (String arg : arguments) {
        output.append(", ").append(arg);
    }
    output.append(");");
    System.out.println("Output: " + output);
  }
}

      

Login: Log.d ("Hello there" + x + ", I see that your" + y + "years!"); Output: Log.d ("Hi% s, I see that you have% s years!", X, y);

Java demo here .

0


source


If you replace everything with %s

, you can do this: (ps: Assuming well formatted code in terms of whitespace)

Move from RIGHT to LEFT as the position of the parameter is important.

1.) Run this regex to allow all forms Log.d({something} + var)

beforeLog.d({something}, var)

(Log\.d\(.*?)\"\s*\+\s*([^\s]+)(\+)?(\))

      

with replacement



$1%s", $2$4

      

( https://regex101.com/r/hY2iK6/8 )

2.) Now you need to take care of all the variables occurring between the lines:

Keep working with this regular expression until the replacements appear:

(Log\.d\(.*)(\"\s*\+\s*([^\s]+)\s*\+\s*\")(.*?\"),([^\"]+);

      

with replacement



$1%s$4,$3,$5;

      

After launch 1: https://regex101.com/r/hY2iK6/10

After Launch 2: https://regex101.com/r/hY2iK6/11

3.) Finally, you need to solve the lines containing the leading variable - this is not a problem:

(Log\.d\()([^\"]+)\s+\+\s*\"(.*?),([^"]+;)

      

with replacement



$1"%s$3,$2,$4

      

https://regex101.com/r/hY2iK6/9

Cases may not be covered, but that should give you an idea.

I added Log.d

to the match groups as well as its replacement part, so you can use Log\.(?:d|f|e)

if you like,

+1


source







All Articles