java regex mask all elements in a list with last 4 characters visible

I have a list of alphanumeric strings as below

["nG5wnyPVNxS6PbbDNNbRsK5zanG94Et6Q4y74","GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1odeNv","GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1o12NN"]

I need to mask all elements with last 4 characters visible and [ " must not be masked as below.

["XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4y74","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXdeNv","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX12NN"]

I have tried using

(\\W+)(\\W+)(\\w+)(\\w+)(\\w+)(\\w+)(\\w+)(\\W+)(\\W+) 

as the key and $1$2XXXXXXXXXX$4$5$6$7$8$9 as the value in

maskedValue = maskedValue.replaceAll("(\\W+)(\\W+)(\\w+)(\\w+)(\\w+)(\\w+)(\\w+)(\\W+)(\\W+)", "$1$2XXXXXXXXXX$4$5$6$7$8$9")

but this only masked the first element.

["XXXXXXXXXXdeNv","nG5wnyPVNxS6PbbDNNbRsK5zanG94Et6Q4y74"]

Any leads are appreciated. Thanks in advance.

Upvotes: 2

Views: 1525

Answers (3)

Inferno
Inferno

Reputation: 1

Assuming each of these strings will start and end with quotes

Algo:

Use a flag or stack data structure to know if it's a starting quote or ending quote. For example: Traverse the string. Initially flag will be false. When you encounter a new quote you have to flip flag and keep traversing till you find other quote. You can do the same with Stack stack = new Stack<>();

Sample workflow:

String str="random";
    
boolean flag = false;
int idx = 0;
List<Pair<Integer, Integer>> indices = new ArrayList<>();
StringBuilder string = new StringBuilder(); // for final string
int start;
int end;
    
while(idx < str.length()){
    
    if (str.charAt(idx) == '"' && !flag){
    // start index of string
    string.append(s.charAt(idx));
    start = idx;
    flag = true;
    }
    
    else if (str.charAt(idx) == '"' && !flag){
    // end index of string
    flag = false;
    end = idx;
char[] mask = new char[end-3-start];
Arrays.fill(mask, 'x');
    string.append(new String(mask)); // need to put 'x' in place
    }
    
    if (!flag){
    string.append(s.charAt(idx));
    }
    
    idx++;
}

Complexity: O(n)

Upvotes: 0

The fourth bird
The fourth bird

Reputation: 163362

For a single value, you could use an assertion to match a word character asserting 4 characters at the end of the string.

\w(?=\w*\w{4}$)

Regex demo | Java demo

String values[] = {"nG5wnyPVNxS6PbbDNNbRsK5zanG94Et6Q4y74","GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1odeNv","GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1o12NN"};
for (String element : values)
    System.out.println(element.replaceAll("\\w(?=\\w*\\w{4}$)", "X"));

Output

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4y74
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXdeNv
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX12NN

For the whole string, you might use a finite quantifier in a positive lookbehind to match the opening " followed by a number of word characters. Then match all the characters that have 4 character before the closing "

"(?<=\"{0,100})\\w(?=\\w*\\w{4}\")"

Regex demo | Java demo

String regex = "(?<=\"{0,100})\\w(?=\\w*\\w{4}\")";
String string = "[\"nG5wnyPVNxS6PbbDNNbRsK5zanG94Et6Q4y74\",\"GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1odeNv\",\"GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1o12NN\"] ";
System.out.println(string.replaceAll(regex, "X"));

Output

["XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4y74","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXdeNv","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX12NN"] 

Upvotes: 2

Tim Biegeleisen
Tim Biegeleisen

Reputation: 521457

Using a stream:

List<String> terms = Arrays.asList(new String[] {
    "nG5wnyPVNxS6PbbDNNbRsK5zanG94Et6Q4y74",
    "GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1odeNv",
    "GgQoDWqP7KtxXeePyyebu5EnNp8XxPC1o12NN"
});
List<String> termsOut = terms.stream()
    .map(t -> String.join("", Collections.nCopies(t.length() - 4, "x")) +
              t.substring(t.length() - 4))
    .collect(Collectors.toList());
System.out.println(termsOut);

This prints:

[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx4y74,
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdeNv, 
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx12NN]

Note that this solution does not even use regex, which means it may outperform a regex based solution.

Upvotes: 1

Related Questions