我正在尝试用多个其他单词替换字符串中的多个单词。字符串是“我有一只猫、一只狗和一只山羊”。
但是,这不会产生“我有一只狗、一只山羊和一只猫”,而是会产生“我有一只猫、一只猫和一只猫”。是否可以在 JavaScript 中同时用多个其他字符串替换多个字符串,从而产生正确的结果?
var str = "I have a cat, a dog, and a goat.";
str = str.replace(/cat/gi, "dog");
str = str.replace(/dog/gi, "goat");
str = str.replace(/goat/gi, "cat");
//this produces "I have a cat, a cat, and a cat"
//but I wanted to produce the string "I have a dog, a goat, and a cat".
cat
不应匹配 catch
的 cat
部分?您应该精确匹配标准。
具体解决方案
您可以使用一个函数来替换每个函数。
var str = "I have a cat, a dog, and a goat.";
var mapObj = {
cat:"dog",
dog:"goat",
goat:"cat"
};
str = str.replace(/cat|dog|goat/gi, function(matched){
return mapObj[matched];
});
概括它
如果您想动态维护正则表达式并将未来的交换添加到地图中,您可以这样做
new RegExp(Object.keys(mapObj).join("|"),"gi");
生成正则表达式。那么它看起来像这样
var mapObj = {cat:"dog",dog:"goat",goat:"cat"};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");
str = str.replace(re, function(matched){
return mapObj[matched];
});
要添加或更改更多替换,您只需编辑地图即可。
使其可重用
如果您希望这是一个通用模式,您可以将其拉出到这样的函数中
function replaceAll(str,mapObj){
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");
return str.replace(re, function(matched){
return mapObj[matched.toLowerCase()];
});
}
因此,您可以将 str 和您想要的替换映射传递给函数,它将返回转换后的字符串。
要确保 Object.keys 在旧版浏览器中工作,请添加一个 polyfill,例如来自 MDN 或 Es5。
作为答案:
寻找最新的答案
如果您在当前示例中使用“单词”,则可以使用非捕获组扩展 Ben McCormick 的答案,并在左侧和右侧添加单词边界 \b
以防止部分匹配。
\b(?:cathy|cat|catch)\b
\b 防止部分匹配的单词边界
(?: 非捕获组 cathy|cat|catch 匹配备选方案之一
cathy|cat|catch 匹配备选方案之一
关闭非捕获组
\b 防止部分匹配的单词边界
原始问题的示例:
let str = "我有一只猫、一只狗和一只山羊。"; const mapObj = {猫:“狗”,狗:“山羊”,山羊:“猫”}; str = str.replace(/\b(?:cat|dog|goat)\b/gi, 匹配 => mapObj[匹配]);控制台.log(str);
评论中的示例似乎效果不佳:
let str = "我有一只猫、一只猫和一只猫。"; const mapObj = {凯茜:“猫”,猫:“抓住”,抓住:“凯茜”}; str = str.replace(/\b(?:cathy|cat|catch)\b/gi, 匹配 => mapObj[匹配]);控制台.log(str);
使用编号的项目以防止再次更换。例如
let str = "I have a %1, a %2, and a %3";
let pets = ["dog","cat", "goat"];
然后
str.replace(/%(\d+)/g, (_, n) => pets[+n-1])
它是如何工作的:- %\d+ 查找 % 之后的数字。括号捕获数字。
这个数字(作为字符串)是 lambda 函数的第二个参数 n。
+n-1 将字符串转换为数字,然后减去 1 以索引 pets 数组。
然后将 %number 替换为数组索引处的字符串。
/g 导致使用每个数字重复调用 lambda 函数,然后将其替换为数组中的字符串。
在现代 JavaScript 中:-
replace_n=(str,...ns)=>str.replace(/%(\d+)/g,(_,n)=>ns[n-1])
I have a dog1, a cat1, and a goat1
,这对于文本 I have a %11, a %21, and a %31
将无法正常工作。
在这种情况下,这可能无法满足您的确切需求,但我发现这是一种替换字符串中多个参数的有用方法,作为一种通用解决方案。它将替换参数的所有实例,无论它们被引用多少次:
String.prototype.fmt = function (hash) {
var string = this, key; for (key in hash) string = string.replace(new RegExp('\\{' + key + '\\}', 'gm'), hash[key]); return string
}
您可以按如下方式调用它:
var person = '{title} {first} {last}'.fmt({ title: 'Agent', first: 'Jack', last: 'Bauer' });
// person = 'Agent Jack Bauer'
'abc'.fmt({'a(b':1})
const arrayOfObjects = [
{ plants: 'men' },
{ smart:'dumb' },
{ peace: 'war' }
]
const sentence = 'plants are smart'
arrayOfObjects.reduce(
(f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence
)
// as a reusable function
const replaceManyStr = (obj, sentence) => obj.reduce((f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence)
const result = replaceManyStr(arrayOfObjects , sentence1)
例子
// //////////// 1. 使用reduce和对象替换 // arrayOfObjects.reduce((f, s) => `${f}`.replace(Object.keys(s) [0], s[Object.keys(s)[0]]), sentence) // 如果在句子中找到,则将 object 中的键替换为其值 // 如果没有找到单词,则不会中断 // 示例const arrayOfObjects = [ { 植物: 'men' }, { smart:'dumb' }, { Peace: 'war' } ] const sentence1 = '植物很聪明' const result1 = arrayOfObjects.reduce((f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence1) console.log(result1) // result1: // 男人很笨// 额外:带有单词和索引数组的字符串插入 python 样式 // 用法 // arrayOfWords.reduce((f, s, i) => `${f}`.replace(`{${i}}` , s), sentence) // 其中 arrayOfWords 包含您要在句子中插入的单词 // 示例 // 替换句子中与 arrayOfWords 中定义的一样多的单词 // 使用 python 类型 {0}、{1} 等符号// 五个替换 const sentence2 = '{0} is {1} and {2} are {3} each {5}' // 但是四个 i n 数组?不会破坏 const words2 = ['man','dumb','plants','smart'] // 会发生什么? const result2 = words2.reduce((f, s, i) => `${f}`.replace(`{${i}}`, s), sentence2) console.log(result2) // result2: / / 人是愚蠢的,植物是聪明的每个 {5} // 替换数组中定义的尽可能多的单词 // 三个替换 const sentence3 = '{0} 是 {1} 和 {2}' // 但五个array const words3 = ['man','dumb','plant','smart'] //会发生什么?不会破坏 const result3 = words3.reduce((f, s, i) => `${f}`.replace(`{${i}}`, s), sentence3) console.log(result3) / /result3: // 人是哑巴,是植物
${f}
而不是 f 作为累加值?
${f}
.replace(new RegExp(Object.keys (s)[0],'g'), s[Object.keys(s)[0]]), sentence1)"
这对我有用:
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
};
function replaceAll(str, map){
for(key in map){
str = str.replaceAll(key, map[key]);
}
return str;
}
//testing...
var str = "bat, ball, cat";
var map = {
'bat' : 'foo',
'ball' : 'boo',
'cat' : 'bar'
};
var new = replaceAll(str, map);
//result: "foo, boo, bar"
使用我的 replace-once 包,您可以执行以下操作:
const replaceOnce = require('replace-once')
var str = 'I have a cat, a dog, and a goat.'
var find = ['cat', 'dog', 'goat']
var replace = ['dog', 'goat', 'cat']
replaceOnce(str, find, replace, 'gi')
//=> 'I have a dog, a goat, and a cat.'
笔记!
如果您使用的是动态提供的映射,那么这里的解决方案都不够用!
在这种情况下,有两种方法可以解决这个问题,(1)使用拆分连接技术,(2)使用带有特殊字符转义技术的正则表达式。
这是一种拆分连接技术,比另一种快得多(至少快 50%):
var str = "我有 {abc} ac|at、ad(og 和 ag[oat] {1} {7} {11。" var mapObj = { 'c|at': "d(og", 'd (og': "g[oat", 'g[oat]': "c|at", }; var entries = Object.entries(mapObj); console.log( entries .reduce( // 替换所有出现的使用拆分连接将文本中的键放入索引占位符 (_str, [key], i) => _str.split(key).join(`{${i}}`), // 操作所有现有索引占位符-like 格式,为了防止混淆 str.replace(/\{(?=\d+\})/g, '{-') ) // 将所有索引占位符替换为所需的替换值 .replace(/\{ (\d+)\}/g, (_,i) => entries[i][1]) // 撤销对类似索引占位符格式的操作 .replace(/\{-(?=\d+\}) /G, '{') );
这个是正则表达式特殊字符转义技术,它也有效,但要慢得多:
var str = "我有 ac|at、ad(og 和 ag[oat]。" var mapObj = { 'c|at': "d(og", 'd(og': "g[oat", ' g[oat]': "c|at", }; console.log( str.replace( new RegExp( // 将对象转换为键数组 Object.keys(mapObj) // 转义搜索键中的任何特殊字符.map(key => key.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')) // 创建正则表达式pattern .join('|'), // 可以使用附加标志。例如 `i` - 不区分大小写的搜索 'g' ), // 对于找到的每个键,替换为适当的值 match => mapObj[match] ));
后者的优点是它也可以与不区分大小写的搜索一起使用。
Object.keys(mapObj).sort((a,b) => b.length - a.length)
轻松解决
以防万一有人想知道为什么原始海报的解决方案不起作用:
var str = "I have a cat, a dog, and a goat.";
str = str.replace(/cat/gi, "dog");
// now str = "I have a dog, a dog, and a goat."
str = str.replace(/dog/gi, "goat");
// now str = "I have a goat, a goat, and a goat."
str = str.replace(/goat/gi, "cat");
// now str = "I have a cat, a cat, and a cat."
This solution 可以调整为仅替换整个单词 - 例如,搜索“cat”时不会找到“catch”、“ducat”或“locator”。这可以通过在正则表达式中每个单词之前和之后的单词字符上使用负向回溯 (?<!\w)
和负向预读 (?!\w)
来完成:
(?<!\w)(cathy|cat|ducat|locator|catch)(?!\w)
JSFiddle 演示:http://jsfiddle.net/mfkv9r8g/1/
试试我的解决方案。随时改进
function multiReplace(strings, regex, replaces) { return str.replace(regex, function(x) { // 检查替换键以防止出错,如果为 false 则返回原始值 return Object.keys(replaces).includes(x ) ? 替换[x] : x; }); } var str = "我有一只猫、一只狗和一只山羊。"; //(json) 使用值替换键 var replaces = { 'Cat': 'dog', 'dog': 'goat', 'goat': 'cat', } console.log(multiReplace(str, /Cat |dog|goat/g, 替换))
用户常规函数定义要替换的模式,然后使用替换函数处理输入字符串,
var i = new RegExp('"{','g'),
j = new RegExp('}"','g'),
k = data.replace(i,'{').replace(j,'}');
RegExp
对象可以解决问题
var i = new RegExp('}','g'), j = new RegExp('{','g'), k = data.replace(i,'{').replace(j,'}');
时,您仍然遇到与提问者的问题相同的问题
var str = "I have a cat, a dog, and a goat.";
str = str.replace(/goat/i, "cat");
// now str = "I have a cat, a dog, and a cat."
str = str.replace(/dog/i, "goat");
// now str = "I have a cat, a goat, and a cat."
str = str.replace(/cat/i, "dog");
// now str = "I have a dog, a goat, and a cat."
我已经修改了 Ben McCormick 的答案以使用您的新测试用例。
我只是在正则表达式中添加了单词边界:
/\b(cathy|cat|catch)\b/gi
“运行代码片段”以查看以下结果:
var str = "我有一只猫、一只猫和一只猫。"; var mapObj = { cathy:"cat", cat:"catch", catch:"cathy" }; str = str.replace(/\b(cathy|cat|catch)\b/gi, function(matched){ return mapObj[matched]; });控制台.log(str);
/\w+/g
作为模式,并且当你的属性不存在时返回匹配的函数目的。另外,我认为 Map 更合适,而不是 standard 对象: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 。最后,toLowerCase()
或 toUpperCase()
的帮助会很有用。
一种可能的解决方案是使用映射器表达式函数。
const regex = /(?:cat|dog|goat)/gmi;
const str = `I have a cat, a dog, and a goat.`;
let mapper = (key) => {
switch (key) {
case "cat":
return "dog"
case "dog":
return "goat";
case "goat":
return "cat"
}
}
let result = str.replace(regex, mapper);
console.log('Substitution result: ', result);
//Substitution result1: I have a dog, a goat, and a cat.
const str = '感谢您为 Stack Overflow 提供答案!' const substr = ['for', 'to'] function boldString(str, substr) { let boldStr boldStr = str substr.map(e => { const strRegExp = new RegExp(e, 'g'); boldStr= boldStr. replace(strRegExp, `${e}`); } ) return boldStr }
您可以使用分隔符查找和替换字符串。
var obj = { 'firstname': 'John', 'lastname': 'Doe' } var text = "你好 {firstname},你的名字是 {firstname},lastname 是 {lastname}" console.log(mutliStringReplace(obj, text)) function mutliStringReplace(object, string) { var val = string var entries = Object.entries(object); entry.forEach((para)=> { var find = '{' + para[0] + '}' var regExp = new RegExp(find,'g') val = val.replace(regExp, para[1]) }) 返回值; }
String.prototype.replaceSome = function() {
var replaceWith = Array.prototype.pop.apply(arguments),
i = 0,
r = this,
l = arguments.length;
for (;i<l;i++) {
r = r.replace(arguments[i],replaceWith);
}
return r;
}
/* 字符串的 replaceSome 方法,它接受我们想要的多个参数,并将所有参数替换为我们指定的最后一个参数 2013 CopyRights 为:Max Ahmed 这是一个示例:
var string = "[hello i want to 'replace x' with eat]";
var replaced = string.replaceSome("]","[","'replace x' with","");
document.write(string + "<br>" + replaced); // returns hello i want to eat (without brackets)
*/
jsFiddle:http://jsfiddle.net/CPj89/
<!DOCTYPE html>
<html>
<body>
<p id="demo">Mr Blue
has a blue house and a blue car.</p>
<button onclick="myFunction()">Try it</button>
<script>
function myFunction() {
var str = document.getElementById("demo").innerHTML;
var res = str.replace(/\n| |car/gi, function myFunction(x){
if(x=='\n'){return x='<br>';}
if(x==' '){return x=' ';}
if(x=='car'){return x='BMW'}
else{return x;}//must need
});
document.getElementById("demo").innerHTML = res;
}
</script>
</body>
</html>
我编写了这个 npm 包 stringinject https://www.npmjs.com/package/stringinject,它允许您执行以下操作
var string = stringInject("this is a {0} string for {1}", ["test", "stringInject"]);
它将用数组项替换 {0} 和 {1} 并返回以下字符串
"this is a test string for stringInject"
或者您可以使用对象键和值替换占位符,如下所示:
var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });
"My username is tjcafferkey on Github"
为此,您可以使用 https://www.npmjs.com/package/union-replacer。它基本上是一个 string.replace(regexp, ...)
对应物,它允许一次进行多次替换,同时保留 string.replace(...)
的全部功能。
披露:我是作者。该库的开发是为了支持更复杂的用户可配置替换,它解决了所有有问题的问题,如捕获组、反向引用和回调函数替换。
上面的解决方案对于精确的字符串替换来说已经足够好了。
通过使用原型函数,我们可以通过传递带有键和值以及可替换文本的对象来轻松替换
String.prototype.replaceAll=function(obj,keydata='key'){ const keys=keydata.split('key'); return Object.entries(obj).reduce((a,[key,val])=> a.replace(`${keys[0]}${key}${keys[1]}`,val),this ) } const data='hids dv sdc sd ${yathin} ${ok}' console.log(data.replaceAll({yathin:12,ok:'hi'},'${key}'))
所有解决方案都可以很好地工作,除非应用于闭包的编程语言(例如 Coda、Excel、电子表格的 REGEXREPLACE
)。
下面我的两个原始解决方案仅使用 1 个连接和 1 个正则表达式。
方法#1:查找替换值
这个想法是如果它们不在字符串中,则附加替换值。然后,使用单个正则表达式,我们执行所有需要的替换:
var str = "我有一只猫、一只狗和一只山羊。"; str = (str+"||||cat,dog,goat").replace( /cat(?=[\s\S]*(dog))|dog(?=[\s\S]*(goat) )|山羊(?=[\s\S]*(cat))|\|\|\|\|.*$/gi, "$1$2$3"); document.body.innerHTML = str;
说明:
cat(?=[\s\S]*(dog)) 表示我们在寻找“猫”。如果匹配,则正向查找将捕获“dog”作为组 1,否则捕获“”。
对于将“goat”捕获为第 2 组的“dog”和将“cat”捕获为第 3 组的“goat”也是如此。
我们替换为“$1$2$3”(所有三个组的串联),对于上述情况之一,它始终是“dog”、“cat”或“goat”
如果我们手动将替换附加到字符串,如 str+"||||cat,dog,goat",我们也会通过匹配 \|\|\|\|.*$ 来删除它们,在这种情况下替换为 "$1$2$3"将评估为“”,即空字符串。
方法#2:查找替换对
方法 #1 的一个问题是它一次不能超过 9 个替换,这是反向传播组的最大数量。方法 #2 声明不要仅附加替换值,而是直接附加替换:
var str = "我有一只猫、一只狗和一只山羊。"; str = (str+"||||,cat=>dog,dog=>goat,goat=>cat").replace( /(\b\w+\b)(?=[\s\S]*,\ 1=>([^,]*))|\|\|\|\|.*$/gi, "$2"); document.body.innerHTML = str;
说明:
(str+"||||,cat=>dog,dog=>goat,goat=>cat") 是我们如何将替换映射附加到字符串的末尾。
(\b\w+\b) 声明“捕获任何单词”,可以用“(cat|dog|goat) 或其他任何内容替换。
(?=[\s\S]*...) 是一个正向查找,通常会到文档的末尾,直到替换映射之后。 ,\1=> 表示“您应该在逗号和右箭头之间找到匹配的单词” ([^,]*) 表示“匹配此箭头之后的任何内容,直到下一个逗号或文档结尾”
,\1=> 表示“您应该在逗号和右箭头之间找到匹配的单词”
([^,]*) 表示“匹配此箭头之后的任何内容,直到下一个逗号或文档结尾”
|\|\|\|\|.*$ 是我们删除替换映射的方式。
我们还可以使用 split() 和 join() 方法:
var str = "我有一只猫、一只狗和一只山羊。"; str=str.split("cat").map(x => {return x.split("dog").map(y => {return y.split("goat").join("cat"); }).join("goat");}).join("dog");控制台.log(str);
你可以试试这个。买不聪明。
var str = "我有一只猫、一只狗和一只山羊。";控制台.log(str); str = str.replace(/cat/gi, "XXX");控制台.log(str); str = str.replace(/goat/gi, "cat");控制台.log(str); str = str.replace(/dog/gi, "山羊");控制台.log(str); str = str.replace(/XXX/gi, "狗");控制台.log(str);
我对@BenMcCormicks 进行了一些扩展。他适用于常规字符串,但如果我有转义字符或通配符,则不会。这就是我所做的
str = "[curl] 6: blah blah 234433 blah blah";
mapObj = {'\\[curl] *': '', '\\d: *': ''};
function replaceAll (str, mapObj) {
var arr = Object.keys(mapObj),
re;
$.each(arr, function (key, value) {
re = new RegExp(value, "g");
str = str.replace(re, function (matched) {
return mapObj[value];
});
});
return str;
}
replaceAll(str, mapObj)
返回“废话 234433 废话”
这样它将匹配 mapObj 中的键而不是匹配的单词'
使用 Jquery 的解决方案(首先包含此文件):将多个字符串替换为多个其他字符串:
var replacetext = {
"abc": "123",
"def": "456"
"ghi": "789"
};
$.each(replacetext, function(txtorig, txtnew) {
$(".eng-to-urd").each(function() {
$(this).text($(this).text().replace(txtorig, txtnew));
});
});
javascript tag added in question, and jquery is a libarary of javascript.
嗯,逻辑已关闭,它应该是另一种方式,也只是提醒 - 来自 javascript 标签信息:“除非还包括框架/库的另一个标签,否则纯 JavaScript 答案预计。"
不定期副业成功案例分享
.
表示法。括号表示法适用于任何字符串。map={'.': ',', ',': '.'}
和正则表达式/\.|,/g
。return mapObj[matched.toLowerCase()];
替换为return mapObj[matched];
,因为我在mapObj
中使用了区分大小写的键。Object.keys(mapObj).map(key => key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')).join('|')
。灵感来自 this answer