0

I'm using replaceAll to only list the numbers in a certain string and I was wondering if there was a way to limit the number of times it replaced. For example:

String s = "1qwerty2qwerty3";
s = s.replaceAll("[^0-9]+", " ");
System.out.println(Arrays.asList(s.trim().split(" ")));

This will filter out all the numbers in a string, giving the result: [1, 2, 3]. I want to know if there is a way to instead get the result [1, 2]. So, basically the method finds two numbers and stops. Thanks for any help!

prashant
  • 348
  • 3
  • 13
amaze
  • 19
  • 7
  • use replace in a loop but replaceAll – Yu Jiaao May 03 '18 at 22:36
  • 1
    Possible duplicate of [in matcher.replace method,how to limit replace times?](https://stackoverflow.com/questions/3448330/in-matcher-replace-method-how-to-limit-replace-times) – Alex Shesterov May 03 '18 at 22:39
  • I think the best idea is to use the String method split​(String regex, int limit) see https://docs.oracle.com/javase/9/docs/api/java/lang/String.html#split-java.lang.String-int- – rickz May 03 '18 at 22:44

2 Answers2

1

Your replaceAll is removing everything that isn't a digit, but you want to limit the numbers returned by the split! I would, instead, stream the result of the split - then you can limit that and collect it to a List. Like,

String s = "1qwerty2qwerty3";
s = s.replaceAll("\\D+", " "); // <-- equivalent to your current regex.
System.out.println(Stream.of(s.split("\\s+")).limit(2).collect(Collectors.toList()));

Outputs (as requested)

[1, 2]

And, we can actually eliminate a step if we split on non-digits to begin with. Like,

System.out.println(Stream.of(s.split("\\D+")).limit(2)
    .collect(Collectors.toList()));
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • Instead of `Stream.of(s.split("\\D+"))`, use `Pattern.compile("\\D+").splitAsStream(s)`. That way, even the splitting operation is short-circuited after 2 splits. – Andreas May 03 '18 at 22:45
  • *Caveat:* Will give initial empty string in result if input text doesn't start with number, and the empty string is counted as one of the two. See [my answer](https://stackoverflow.com/a/50164848/5221149). – Andreas May 03 '18 at 23:10
  • This caveat happened for me too, though I don't see it as too big of an issue. – amaze May 03 '18 at 23:44
0

Rather than splitting away what you don't want, search for what you do want.

In Java 9+, this can be done easily with streams:

String s = "1qwerty2qwerty3";
System.out.println(Pattern.compile("\\d+")
                          .matcher(s)
                          .results()
                          .limit(2)
                          .map(MatchResult::group)
                          .collect(Collectors.toList()));

// or condensed:
System.out.println(Pattern.compile("\\d+").matcher(s).results()
    .limit(2).map(r->r.group()).collect(Collectors.toList()));

Disclaimer: Idea of using limit() taken from answer by Elliott Frisch.

In Java 5+, you need a find() loop:

List<String> result = new ArrayList<String>();
for (Matcher m = Pattern.compile("\\d+").matcher(s); m.find(); ) {
    result.add(m.group());
    if (result.size() == 2)
        break;
}
System.out.println(result);

Output from both

[1, 2]

The advantage of these, over solution using split(), is that you won't get an empty string first, if input doesn't start with a digit.

Example

String s = "qwerty1qwerty2qwerty3qwerty";

// Using this answer
System.out.println(Pattern.compile("\\d+")
                          .matcher(s)
                          .results()
                          .limit(2)
                          .map(MatchResult::group)
                          .collect(Collectors.toList()));

// Using answer by Elliott Frisch
System.out.println(Stream.of(s.split("\\D+")).limit(2)
    .collect(Collectors.toList()));

// Alternate, applying comment to answer by Elliott Frisch
System.out.println(Pattern.compile("\\D+")
                          .splitAsStream(s)
                          .limit(2)
                          .collect(Collectors.toList()));

Output

[1, 2]
[, 1]
[, 1]
Andreas
  • 154,647
  • 11
  • 152
  • 247