Reputation: 21
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
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
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}$)
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}\")"
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
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