11

I have a string of comma separated words e.g. "a, b, c".
I need to replace the last comma instance with “and”, to make it look like "a, b and c".

I've tried using replaceFirst like this:

"a, b, c".replaceFirst("(,)[^,]+$", " and")

But it doesn't work. It replaces everything after the last comma with “and” rather than just the comma producing "a, b and".

How to make it work?
I'm on java8.

Ps. I thought that it's obvious, but it seems like I need to clarify, that I'm looking for a generic solution that works with any number of comma separated tokens, i.e. 'a, b, ...., c'

6
  • 3
    String's 'lastIndexOf' would help Commented Apr 6, 2017 at 6:21
  • i'm also thinking of lastIndexOf but it can't be use on replace right? Commented Apr 6, 2017 at 6:24
  • 2
    Use "a, b, c".replaceFirst(",([^,]+)$", " and$1") Commented Apr 6, 2017 at 8:13
  • @Holger haha,we are the same. Commented Apr 6, 2017 at 8:31
  • 1
    @holi-java: I already noticed. It’s the most straight-forward way to fix the question’s approach, imho. Commented Apr 6, 2017 at 8:33

3 Answers 3

12

I don't think you need anything particular from Java 8 here. If you want to go the regex route with String#replaceAll(), then you can consider using the following pattern:

(.*), (.*)

Simple rebuild the string using the first and second capture groups, with and in between them. The first pattern (.*) will greedily consume everything until the last comma, exactly the behavior you want. Then, the second (.*) does cleanup to capture the final letter/string.

String input = "a, b, c";
input = input.replaceAll("(.*), (.*)", "$1 and $2");
System.out.println(input);

Output:

a, b and c
Sign up to request clarification or add additional context in comments.

1 Comment

You might add why it works: The first .* is greedy, so consumes all but the last ,. (Well, they're both greedy, but the earlier one gets to be greedy first.)
8

I think the replaceFirst is better than replaceAll for you because you want to replace only once not all, and it run faster than replaceAll.

  1. using ${number} to capturing groups.

    "a, b, c".replaceFirst(",([^,]+)$", " and$1"); // return "a, b and c"
    
  2. using positive lookahead:

    "a, b, c".replaceFirst(",(?=[^,]+$)", " and"); // return "a, b and c"
    

4 Comments

I did not down-vote, but that replaceFirst is quiet misleading; it's actually a single replace there... May be replacing with replaceAll would make more sense.
@Eugene I think replaceFirst better meaniful than replaceAll, because OP want to replace once. thanks, I want to know why vote it down.
@Holger I don't care about the votes, I want to know why vote it down? can you tell me why?
@Holger I have removed it from answer, You may have misunderstood me. I just want to know why ? and learning more from why vote it down.
2

You can also achieve same thing using lastIndexOf :

String str = "a, b, c";
StringBuilder builder = new StringBuilder(str);
int lastindex = str.lastIndexOf(",");
builder.replace(lastindex, lastindex + 1, " and" );
System.out.println(builder.toString());

Output:

a, b and c

1 Comment

Sometimes, a lightweight StringBuilder operation might be preferable to a regex manipulation, but then, it would be more efficient to use partial copying instead of copying the entire string and shifting content: int lastindex = str.lastIndexOf(','); String result = new StringBuilder(str.length()+3) .append(str, 0, lastindex).append(" and") .append(str, lastindex+1, str.length()) .toString();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.