71

Is there replaceLast() in Java? I saw there is replaceFirst().

EDIT: If there is not in the SDK, what would be a good implementation?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Carlos Blanco
  • 8,592
  • 17
  • 71
  • 101
  • 5
    People here are way too eager to mark questions duplicates. This question (general replaceAll()) has nothing to do with the question in the "duplicate". – Esko Piirainen Aug 29 '17 at 10:14

12 Answers12

66

It could (of course) be done with regex:

public class Test {

    public static String replaceLast(String text, String regex, String replacement) {
        return text.replaceFirst("(?s)"+regex+"(?!.*?"+regex+")", replacement);
    }

    public static void main(String[] args) {
        System.out.println(replaceLast("foo AB bar AB done", "AB", "--"));
    }
}

although a bit cpu-cycle-hungry with the look-aheads, but that will only be an issue when working with very large strings (and many occurrences of the regex being searched for).

A short explanation (in case of the regex being AB):

(?s)     # enable dot-all option
A        # match the character 'A'
B        # match the character 'B'
(?!      # start negative look ahead
  .*?    #   match any character and repeat it zero or more times, reluctantly
  A      #   match the character 'A'
  B      #   match the character 'B'
)        # end negative look ahead

EDIT

Sorry to wake up an old post. But this is only for non-overlapping instances. For example .replaceLast("aaabbb", "bb", "xx"); returns "aaaxxb", not "aaabxx"

True, that could be fixed as follows:

public class Test {

    public static String replaceLast(String text, String regex, String replacement) {
        return text.replaceFirst("(?s)(.*)" + regex, "$1" + replacement);
    }

    public static void main(String[] args) {
        System.out.println(replaceLast("aaabbb", "bb", "xx"));
    }
}
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • 4
    Sorry to wake up an old post. But this is only for non-overlapping instances. For example `.replaceLast("aaabbb", "bb", "xx");` returns `"aaaxxb"`, not `"aabxx"` – ColBeseder Oct 31 '13 at 12:16
  • A class implementing this: https://github.com/Fewlaps/quitnow-email-suggester/blob/master/src/com/fewlaps/quitnowemailsuggester/util/StringUtils.java And its test: https://github.com/Fewlaps/quitnow-email-suggester/blob/master/src/test/StringUtilsTest.java BTW, thanks, @BartKiers! – Roc Boronat Aug 28 '15 at 19:04
45

If you don't need regex, here's a substring alternative.

public static String replaceLast(String string, String toReplace, String replacement) {
    int pos = string.lastIndexOf(toReplace);
    if (pos > -1) {
        return string.substring(0, pos)
             + replacement
             + string.substring(pos + toReplace.length());
    } else {
        return string;
    }
}

Testcase:

public static void main(String[] args) throws Exception {
    System.out.println(replaceLast("foobarfoobar", "foo", "bar")); // foobarbarbar
    System.out.println(replaceLast("foobarbarbar", "foo", "bar")); // barbarbarbar
    System.out.println(replaceLast("foobarfoobar", "faa", "bar")); // foobarfoobar
}
Veer
  • 1,373
  • 1
  • 12
  • 27
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
22

use replaceAll and add a dollar sign right after your pattern:

replaceAll("pattern$", replacement);
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
PhantomKid
  • 253
  • 2
  • 3
  • 9
    This only works if the last instance is the last value. i.e., s = s.replaceAll(".1$", ".2"); hi.1 would be hi.2, but if I did hi.12 it would still be hi.12 "hi.1" -> "hi.2" (good) "hi.12" -> "hi.12" (Bad) "hi.11" -> "hi..2" (Bad) "hi.111" -> "hi.1.2" (Bad) "hi.1111" -> "hi.11.2" (Bad) The good thing for me is, I need it to be the last instance, and it will be the last value :). Just wanted to note this for those who might find this answer. – XaolingBao Dec 03 '15 at 23:38
9

You can combine StringUtils.reverse() with String.replaceFirst()

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Mihir Mathuria
  • 6,479
  • 1
  • 22
  • 15
8

See for yourself: String

Or is your question actually "How do I implement a replaceLast()?"

Let me attempt an implementation (this should behave pretty much like replaceFirst(), so it should support regexes and backreferences in the replacement String):

public static String replaceLast(String input, String regex, String replacement) {
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(input);
    if (!matcher.find()) {
       return input;
    }
    int lastMatchStart=0;
    do {
      lastMatchStart=matcher.start();
    } while (matcher.find());
    matcher.find(lastMatchStart);
    StringBuffer sb = new StringBuffer(input.length());
    matcher.appendReplacement(sb, replacement);
    matcher.appendTail(sb);
    return sb.toString();
}
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
8

Use StringUtils from apache:

org.apache.commons.lang.StringUtils.chomp(value, ignoreChar);
Alex Escu
  • 147
  • 1
  • 6
  • This is (or will be) [deprecated](https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html#chomp-java.lang.String-java.lang.String-) – mleonard Nov 16 '16 at 13:22
  • 6
    deprecated in favour of `StringUtils.removeEnd` which works fine. – Kalle Richter Dec 27 '16 at 21:48
3

No.

You could do reverse / replaceFirst / reverse, but it's a bit expensive.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Thomas
  • 17,016
  • 4
  • 46
  • 70
  • 2
    + you will need to reverse the search string, so that's 3 reverse operations. – Fortega Feb 17 '10 at 17:14
  • 1
    Not even *"reverse the search string"* because it's a regex, you will have to **modify** the regex to cater to the reversed string (reversed regex is most likely garbage). – ADTC Aug 15 '14 at 03:50
  • ...err... or write the regex correctly (i.e., to match the reversed string) in the first place? – Thomas Apr 24 '19 at 12:44
2

If the inspected string is so that

myString.endsWith(substringToReplace) == true

you also can do

myString=myString.replaceFirst("(.*)"+myEnd+"$","$1"+replacement) 
Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • 1
    Why did you capture? Can't you just `myString=myString.replaceFirst(myEnd+"$", replacement)`? – DavidS Dec 04 '14 at 18:46
0

it is slow, but works:3

    import org.apache.commons.lang.StringUtils;

public static String replaceLast(String str, String oldValue, String newValue) {
    str = StringUtils.reverse(str);
    str = str.replaceFirst(StringUtils.reverse(oldValue), StringUtils.reverse(newValue));
    str = StringUtils.reverse(str);
    return str;
}
AvrDragon
  • 7,139
  • 4
  • 27
  • 42
0

split the haystack by your needle using a lookahead regex and replace the last element of the array, then join them back together :D

String haystack = "haystack haystack haystack";
String lookFor = "hay";
String replaceWith = "wood";

String[] matches = haystack.split("(?=" + lookFor + ")");
matches[matches.length - 1] = matches[matches.length - 1].replace(lookFor, replaceWith);
String brandNew = StringUtils.join(matches);
MushyPeas
  • 2,469
  • 2
  • 31
  • 48
0

I also have encountered such a problem, but I use this method:

public static String replaceLast2(String text,String regex,String replacement){
    int i = text.length();
    int j = regex.length();

    if(i<j){
        return text;
    }

    while (i>j&&!(text.substring(i-j, i).equals(regex))) {
        i--;
    }

    if(i<=j&&!(text.substring(i-j, i).equals(regex))){
        return text;
    }

    StringBuilder sb = new StringBuilder();
    sb.append(text.substring(0, i-j));
    sb.append(replacement);
    sb.append(text.substring(i));

    return sb.toString();
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
xhay
  • 1
0

It really works good. Just add your string where u want to replace string in s and in place of "he" place the sub string u want to replace and in place of "mt" place the sub string you want in your new string.

import java.util.Scanner;

public class FindSubStr 
{
 public static void main(String str[])
 {
    Scanner on=new Scanner(System.in);
    String s=on.nextLine().toLowerCase();
    String st1=s.substring(0, s.lastIndexOf("he"));
    String st2=s.substring(s.lastIndexOf("he"));
    String n=st2.replace("he","mt");

    System.out.println(st1+n);
 }

}