JavaScript:替换字符串中最后一次出现的文本

147

请看下面的代码片段:

var list = ['one', 'two', 'three', 'four'];
var str = 'one two, one three, one four, one';
for ( var i = 0; i < list.length; i++)
{
     if (str.endsWith(list[i])
     {
         str = str.replace(list[i], 'finish')
     }
 }
我想将字符串中最后一个单词“one”替换为“finish”,但我现在的方法不起作用,因为replace方法只会替换第一个。有谁知道我该如何修改代码片段,以便它仅替换最后一个“one”的实例?
16个回答

164

如果字符串确实以该模式结尾,你可以这样做:

str = str.replace(new RegExp(list[i] + '$'), 'finish');

4
好主意。需要先转义该字符串(虽然在她的示例数据中不需要,但这是非常棘手的)。 :-( - T.J. Crowder
1
@Ruth 没问题!@TJ 是的,确实如此:Ruth如果你最终得到了包含正则表达式特殊字符的“单词”,你需要“转义”它们,正如TJ所说有点棘手(但不是不可能)。 - Pointy
1
如果您想替换字符串2中最后一次出现的字符串1,该怎么办? - SuperUberDuper
1
@SuperUberDuper 如果你想要匹配被“/”字符包围的内容,那么这个方法就非常有用。创建正则表达式实例有两种方式:使用构造函数(new RegExp(string))或使用行内语法(/something/)。如果你使用RegExp构造函数,就不需要加上“/”字符。 - Pointy
3
@TechLife,要使正则表达式中的特殊字符(如+、*、?、括号、方括号等)得到保护,你需要对字符串应用一种转换,并在这些特殊字符前加上反斜杠 \。可能还有其他需要保护的字符。 - Pointy
显示剩余7条评论

51
你可以使用 String#lastIndexOf 查找单词的最后一个出现位置,然后使用 String#substring 和字符串连接来构建替换字符串。
n = str.lastIndexOf(list[i]);
if (n >= 0 && n + list[i].length >= str.length) {
    str = str.substring(0, n) + "finish";
}

...或类似的内容。


3
另一个例子: var nameSplit = item.name.lastIndexOf(", "); if (nameSplit != -1) item.name = item.name.substr(0, nameSplit) + " 和 "+ item.name.substr(nameSplit + 2); - Ben Gotow

16

不像以上的正则表达式答案那么优雅,但对我们中的不太熟练者更易于理解:

function removeLastInstance(badtext, str) {
    var charpos = str.lastIndexOf(badtext);
    if (charpos<0) return str;
    ptone = str.substring(0,charpos);
    pttwo = str.substring(charpos+(badtext.length));
    return (ptone+pttwo);
}

我认识到这可能比正则表达式示例更慢、更浪费,但我认为它可能有助于说明字符串操作的实现方式。(它也可以被压缩一些,但再次强调,我希望每个步骤都清晰可见。)


14

这里有一个只使用分割和连接的方法。它更易读,所以觉得值得分享:

    String.prototype.replaceLast = function (what, replacement) {
        var pcs = this.split(what);
        var lastPc = pcs.pop();
        return pcs.join(what) + replacement + lastPc;
    };

3
它的功能很好,但是如果字符串中根本没有包含“what”,它将在字符串开头添加替换内容。例如:'foo'.replaceLast('bar'); // 'barfoo'。 - Adam Pietrasiak
3
请避免原型污染。 - sean

12
一个简单的一行代码答案,不需要任何正则表达式就可以实现:
str = str.substring(0, str.lastIndexOf(list[i])) + 'finish'

2
如果原始字符串中没有出现怎么办? - tomazahlin
最被接受的答案返回与我的相同的结果!这是测试结果: - Tim Long
我的:> s = s.substr(0, s.lastIndexOf('d')) + 'finish' => 'finish' ... 他们的:> s = s.replace(new RegExp('d' + '$'), 'finish') => 'finish' - Tim Long
这是最好的答案。 - doublejosh
иҝҷжҳҜдёҖдёӘдёҚй”ҷзҡ„еӣһзӯ”пјҢдҪҶжҳҜsubstr()е·Із»Ҹиў«ејғз”ЁдәҶгҖӮжҲ‘и®ӨдёәдҪ еҸҜд»Ҙз”Ёsubstring()жқҘжӣҝжҚўе®ғгҖӮ - Robin Zimmermann

11

这里我想回答一下,因为这是我在谷歌搜索中找到的第一个结果,并且没有一个通用的答案(除了 Matt 的创意答案 :)),可以替换字符串的最后一个出现位置,当需要替换的文本可能不在字符串的末尾时。

if (!String.prototype.replaceLast) {
    String.prototype.replaceLast = function(find, replace) {
        var index = this.lastIndexOf(find);

        if (index >= 0) {
            return this.substring(0, index) + replace + this.substring(index + find.length);
        }

        return this.toString();
    };
}

var str = 'one two, one three, one four, one';

// outputs: one two, one three, one four, finish
console.log(str.replaceLast('one', 'finish'));

// outputs: one two, one three, one four; one
console.log(str.replaceLast(',', ';'));

7

我不喜欢以上任何答案,下面是我的想法:

function isString(variable) { 
    return typeof (variable) === 'string'; 
}

function replaceLastOccurrenceInString(input, find, replaceWith) {
    if (!isString(input) || !isString(find) || !isString(replaceWith)) {
        // returns input on invalid arguments
        return input;
    }

    const lastIndex = input.lastIndexOf(find);
    if (lastIndex < 0) {
        return input;
    }

    return input.substr(0, lastIndex) + replaceWith + input.substr(lastIndex + find.length);
}

使用方法:

const input = 'ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty';
const find = 'teen';
const replaceWith = 'teenhundred';

const output = replaceLastOccurrenceInString(input, find, replaceWith);
console.log(output);

// output: ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteenhundred twenty


希望这有所帮助!

6
一种负向前瞻的解决方案:
str.replace(/(one)(?!.*\1)/, 'finish')

这是一个由regex101.com网站提供的解释。

/(one)(?!.*\1)/

第一个捕获组(one)
  • one - 匹配字符“one”(区分大小写)
否定的先行断言 (?!.*\1)

断言以下正则表达式不匹配

  • . 匹配除换行符以外的任意字符
  • * 匹配前面的元素零次或多次,尽可能多地匹配,直到必要时回溯(贪婪模式)
  • \1 匹配与第一个捕获组最近匹配的相同文本

2
这可能对大多数阅读此文的人来说太聪明了!: P) - Aurovrata
最佳答案,因为其他答案没有考虑到“最后出现”并不意味着“句子结束”,而且在例如“一加一等于负一加三”的情况下会失败。 - OfirD
最佳答案,因为其他答案没有考虑到“最后出现”并不意味着“句子结束”,并且在例如“一加一等于负一加三”的情况下失败了。 - undefined
在这种情况下(重复的符号)不起作用:"ugy00000.jpg".replace(/(00)(?!.*\1)/, 55) - oleedd
这个方法非常巧妙地防止了新行上的孤立单词,太棒了——someText.replace(/(\s)(?!.*\1)/, ' ')(替换字符串中有一个不间断空格字符)。通过插入这个字符,任何文本都会以"last word."结尾,确保"last"和"word."会放在同一行上。 - undefined

3

你能不能只翻转字符串并仅替换翻转后搜索模式的第一个出现位置?我在想……

var list = ['one', 'two', 'three', 'four'];
var str = 'one two, one three, one four, one';
for ( var i = 0; i < list.length; i++)
{
     if (str.endsWith(list[i])
     {
         var reversedHaystack = str.split('').reverse().join('');
         var reversedNeedle = list[i].split('').reverse().join('');

         reversedHaystack = reversedHaystack.replace(reversedNeedle, 'hsinif');
         str = reversedHaystack.split('').reverse().join('');
     }
 }

3

简单的解决方案是使用substring方法。 由于字符串以列表元素结尾,我们可以使用字符串长度来计算子字符串的结束索引,而无需使用lastIndexOf方法。

str = str.substring(0, str.length - list[i].length) + "finish"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接