简体   繁体   中英

Java: Substitution of numbers on a given String with the correspondent ID

I' ve got a String like this:

"[1] ,[2,4], [1,2,3] ,[12,42]..." that has non predictable structure (ie there could be more parentheses or more numbers inside them).

The numbers correspond to a certain id that is contained in a HashMap. For example:

1->Apollo11

2->NewYork12

4-> Hello3

...

42->Joe4

...

How could I substitute the numbers in the string with their corresponding id, without ambiguity? For example if I substitute number by number I could have:

Substitution of 1-> even the 1 belonging to 12 is affected.

If I start from the biggest number, ie 42, when 4 is substituted, even the "4" in Joe4 will be affected.

Thanks for your attention.

Simply replacing occurences of the numbers by the associated names would have several drawbacks as you've already found out:

  • you'd need several passes which could mean a significant performance hit if your string is large or you have a large number of pairs
  • if you'd replace numbers from large to small you could still run into problems if for example you only have a mapping for 4 and thus you'd still replace the 4 in 42
  • using regular expressions you could match numbers that aren't surrounded by characters or other numbers only but that might further increase the performance hit and still is somewhat fragile, eg if you'd replace 1 with Apollo 11 and later replace 11 .

Thus probably the safest approach would be to split your string at the numbers, iterate over the tokens, replace any numbers and re-join the elements.

Example:

Map<String, String> mapping = ...;
String input = "[1] ,[2,4], [1,2,3] ,[12,42]";
String[] elements = input.split("(?<=\\d++|\\D++)");
StringBuilder result = new StringBuilder();
for( String element : elements ) {
  //simple way to get around checking whether an element is a number: 
  //if there is nothing mapped, use the element itself - this also keeps unmapped numbers
  String replacement = mapping.get( element );
  result.appdend( replacement != null ? replacement : element );
}

Some words on the regex used to split:

  • (?<=...) is a zero-width look behind, ie it matches any position directly before any match of the ... expression.
  • \\d++|\\D++ matches any sequence of digits or non-digits with a possive quantifier (ie matches as much as possible and doesn't "give back" the match).

The entire expression matches the positions right before your numbers and right before anything that's not a number (or in other words: after your numbers).

Here is my solution (not polished, but I hope you get the idea). Due of the absence of lookup values I just replace all values with **value** as an example:

public static void main(String[] args) {
    String input = "[1] ,[2,4], [1,2,3] ,[12,42]...";
    int start = -1;
    StringBuilder builder = new StringBuilder();
    for (int idx = 0; idx < input.length(); idx++) {
        char c = input.charAt(idx);
        if (start == -1) {
            if (Character.isDigit(c)) {
                start = idx;
            } else {
                builder.append(c);
            }
        } else if (!Character.isDigit(c)) {
            builder.append(valueOf(input.substring(start, idx)) + c);
            start = -1;
        }
    }
    if (start != -1) {
        builder.append(valueOf(input.substring(start, input.length())));
    }
    System.out.println(builder);
}

private static String valueOf(String num) {
    int val = Integer.parseInt(num);
    return map.getOrDefault(val,"**" + val + "**");
}

Output:

[**1**] ,[**2**,**4**], [**1**,**2**,**3**] ,[**12**,**42**]...

You have to replace the expression map.getOrDefault(val,"**" + val + "**") with the reference to your map where the replacement strings are held.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM