其他教程

其他教程

Products

当前位置:首页 > 其他教程 >

Golang正则表达式匹配并替换某个字符串后的第一个匹配项

GG网络技术分享 2025-03-18 16:15 2


问题描述:

Following up on my previous question about using Golang\'s regex to replace between strings. I now have a bit of complexity added to it. Here is what the contexts of my file looks like:

foo:

blahblah

MYSTRING=*

bar:

blah

blah

MYSTRING=*

I need to replace what\'s between MYSTRING= and with a string of my choice (like previous stated in the original post). I can do that with:

var re = regexp.MustCompile(`(MYSTRING=).*`)

s := re.ReplaceAllString(content, `${1}stringofmychoice`)

But now I need to match and replace only after a certain occurrence. So that the contents of my file can look something like this:

foo:

blahblah

MYSTRING=foostring

bar:

blah

blah

MYSTRING=barstring

ReplaceAllString obviously replaces everything, which is not what I want. Is there a way to only match and replace the first occurrence after a certain string?


For a bit of background about all of this. I\'m trying to write a program to edit the contents of a given docker-compose.yml file and its environment variables. I need to edit the environment variable MYSTRING differently depending on what service it\'s listed under. In the example above, the two different services would be foo and bar.

图片转代码服务由CSDN问答提供

感谢您的意见,我们尽快改进~

功能建议

紧随我之前的有关使用Golang的正则表达式替换字符串之间的问题。 现在,我增加了一些复杂性。 这是我文件的上下文:</ p>

  foo:

blahblah

MYSTRING = *

bar:

blah

blah

MYSTRING = * \\ n </ code> </ pre>

我需要用我选择的字符串替换 MYSTRING = </ code>和

</ code>之间的内容(例如上一个 在原始帖子中说明)。 我可以这样:</ p>

  var re = regexp.MustCompile(`(MYSTRING =)。*`)

s:= re.ReplaceAllString(content,`$ {1 } stringofmychoice`)

</ code> </ pre>

但是现在我只需要匹配并替换就可以了。 这样我的文件内容就可以像这样:</ p>

  foo:

blahblah

MYSTRING = foostring

bar:

blah

blah

MYSTRING = barstring

</ code> </ pre>

ReplaceAllString </ code>显然替换了所有内容,这不是我想要的。 有没有办法只匹配并替换某个字符串后的第一个匹配项?</ p>


有关所有这些的一些背景知识。 我正在尝试编写一个程序来编辑给定 docker-compose.yml </ code>文件及其环境变量的内容。 我需要根据下面列出的服务来不同地编辑环境变量 MYSTRING </ code>。 在上面的示例中,两个不同的服务将是 foo </ code>和 bar </ code>。</ p>

</ div>

网友观点:

You may use ReplaceAllStringFunc and use a regex like

(?m)^bar:(?:

\\s{4}.*)+

See the regex demo. It will match a bar block indented with four whitespaces. Then, after a match is obtained, you may use a regular ReplaceAllString on the match.

See the Go demo:

package main

import (

\\\"fmt\\\"

\\\"regexp\\\"

)

const sample = `foo:

blahblah

MYSTRING=*

bar:

blah

blah

MYSTRING=*`

func main() {

re := regexp.MustCompile(`(?m)^bar:(?:

\\s{4}.*)+`)

re_2 := regexp.MustCompile(`(MYSTRING=).*`)

s := re.ReplaceAllStringFunc(sample, func(m string) string {

return re_2.ReplaceAllString(m, `${1}stringofmychoice`)

})

fmt.Println(s)

}

Here, the second occurrence is changed in the bar block:

foo:

blahblah

MYSTRING=*

bar:

blah

blah

MYSTRING=stringofmychoice

整理正则

元字符 描述

元字符

元字符(Metacharacter)是拥有特殊含义的字符:

. 查找单个字符,除了换行和行结束符。
\\w 查找单词字符。
\\W 查找非单词字符。
\\d 查找数字。
\\D 查找非数字字符。
\\s 查找空白字符。
\\S 查找非空白字符。
\\b 匹配单词边界。
\\B 匹配非单词边界。
\\0 查找 NULL 字符。
\\n 查找换行符。
\\f 查找换页符。
\\r 查找回车符。
\\t 查找制表符。
\\v 查找垂直制表符。
\\xxx 查找以八进制数 xxx 规定的字符。
\\xdd 查找以十六进制数 dd 规定的字符。
\\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

#### 修饰符 描述

i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

#### 量词 描述

量词 描述

n+
匹配任何包含至少一个 n 的字符串。
例如,/a+/ 匹配 "candy" 中的 "a","caaaaaaandy" 中所有的 "a"。
n*
匹配任何包含零个或多个 n 的字符串。
例如,/bo*/ 匹配 "A ghost booooed" 中的 "boooo","A bird warbled" 中的 "b",但是不匹配 "A goat grunted"。
n?
匹配任何包含零个或一个 n 的字符串。
例如,/e?le?/ 匹配 "angel" 中的 "el","angle" 中的 "le"。
n{X}
匹配包含 X 个 n 的序列的字符串。
例如,/a{2}/ 不匹配 "candy," 中的 "a",但是匹配 "caandy," 中的两个 "a",且匹配 "caaandy." 中的前两个 "a"。
n{X,}
X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。
例如,/a{2,}/ 不匹配 "candy" 中的 "a",但是匹配 "caandy" 和 "caaaaaaandy." 中所有的 "a"。
n{X,Y}
X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。
例如,/a{1,3}/ 不匹配 "cndy",匹配 "candy," 中的 "a","caandy," 中的两个 "a",匹配 "caaaaaaandy" 中的前面三个 "a"。注意,当匹配 "caaaaaaandy" 时,即使原始字符串拥有更多的 "a",匹配项也是 "aaa"。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。

#### 方括号用于查找某个范围内的字符:

表达式 描述

[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
[adgk] 查找给定集合内的任何字符。
[^adgk] 查找给定集合外的任何字符。
(red|blue|green) 查找任何指定的选项

#### RegExp 对象方法

方法 描述
exec 检索字符串中指定的值。返回找到的值,并确定其位置。
test 检索字符串中指定的值。返回 true 或 false。
toString 返回正则表达式的字符串。
#### 支持正则表达式的 String 对象的方法
search 检索与正则表达式相匹配的值。
match 找到一个或多个正则表达式的匹配。
replace 替换与正则表达式匹配的子串。
split 把字符串分割为字符串数组。
#### RegExp 对象属性
属性 描述
constructor 返回一个函数,该函数是一个创建 RegExp 对象的原型。
global 判断是否设置了 "g" 修饰符
ignoreCase 判断是否设置了 "i" 修饰符
lastIndex 用于规定下次匹配的起始位置
multiline 判断是否设置了 "m" 修饰符
source 返回正则表达式的匹配模式

![fe4c3b738203e010d80b6e95f00fa2fb.png](evernotecid://14CFAE30-4441-40A8-9461-E2D44A11A958/appyinxiangcom/22264124/ENResource/p32)

#### 使用match

match 是String实例方法。
它的作用是根据参数返回匹配结果,取名match也是非常恰当了。
它接受一个正则表达式作为唯一参数。
可是字符串也可以作为参数怎么解释?
>'abc'.match('b');
>// ["b", index: 1, input: "abc", groups: undefined]
这是因为match方法内部会隐式的调用 new RegExp() 将其转换成一个正则实例。
match方法的返回值可以分为三种情况。
1.**匹配失败**
没什么可说的,返回 null 。
2.**非全局匹配**
返回一个数组。
数组的第一项是匹配结果。如果不传参则匹配结果为空字符串。
>'abc'.match();
>// ["", index: 0, input: "abc", groups: undefined]
如果正则参数中有捕获组,捕获的结果在数组中从第二项开始依次排列。有捕获组但是没有捕获内容则显示 undefined 。
`'@abc2018'.match(/@([a-z]+)([A-Z]+)?/);`
`// ["@abc", "abc", undefined, index: 0, input: "@abc2018", groups: undefined]`
数组有一个 index 属性,标明匹配结果在文本中的起始位置。
数组有一个 input 属性,显示源文本。
数组有一个 groups 属性,它存储的不是捕获组的信息,而是捕获命名的信息。
>`
>'@abc2018'.match(/@(?<lowerCase>[a-z]+)(?<upperCase>[A-Z]+)?/);`
>
>`// ["@abc", "abc", undefined, index: 0, input: "@abc2018", groups: { lowerCase: "abc", upperCase: undefined }]`
3.**全局匹配**
返回一个数组。
捕获的若干结果在数组中依次排列。因为要返回所有匹配的结果,其他的信息,包括捕获组和若干属性就无法列出了。
>'abc&mno&xyz'.match(/[a-z]+/g);
>// ["abc", "mno", "xyz"]
#### replace方法
replace 是String实例方法。
它的作用是将给定字符串替换匹配结果,并返回新的替换后的文本。源文本不会改变。
它接受两个参数。
第一个参数可以是字符串或者正则表达式,它的作用是匹配。
参数是字符串和参数是正则表达式的区别在于:正则表达式的表达能力更强,而且可以全局匹配。因此参数是字符串的话只能进行一次替换。
>'abc-xyz-abc'.replace('abc', 'biu');
>// "biu-xyz-abc"
>'abc-xyz-abc'.replace(/abc/, 'biu');
>// "biu-xyz-abc"
>'abc-xyz-abc'.replace(/abc/g, 'biu');
>// "biu-xyz-biu"
第二个参数可以是字符串或者函数,它的作用是替换。
**第二个参数是字符串**
replace方法为第二个参数是字符串的方式提供了一些特殊的变量,能满足一般需求。
$数字 代表相应顺序的捕获组。注意,虽然它是一个变量,但是不要写成模板字符串`${$1}biu`,replace内部逻辑会自动解析字符串,提取出变量。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '$1biu');
// "@biu-xyz-$biu"
$& 代表匹配结果。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '{$&}');
// <a href="mailto:{@abc}-xyz-{$abc}">{@abc}-xyz-{$abc}</a>
$`代表匹配结果左边的文本。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '{$`}');
// <a href="mailto:{}-xyz-{@abc-xyz-}">{}-xyz-{@abc-xyz-}</a>
$' 代表匹配结果右边的文本。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, "{$'}");
// "{-xyz-$abc}-xyz-{}"
有些时候我要的是变量的符号本身,而不是它的变量值,怎么办?加一个 $ 转义一下。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '$$1biu');
// "$1biu-xyz-$1biu"
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '$biu');
// "$biu-xyz-$biu"
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, '$$biu');
// "$biu-xyz-$biu"
在不会造成误会的场景,一个 $ 和两个 $ 的效果都是一个 $ ,因为另一个充当转义符号。会造成误会的场景,那就必须加 $ 转义了。
第二个参数是函数
字符串的变量毕竟只能引用,无法操作。与之相对,函数的表达能力就强多了。
函数的返回值就是要替换的内容。函数如果没有返回值,默认返回 undefined ,所以替换内容就是 undefined 。
函数的第一个参数,是匹配结果。
'abc-xyz-abc'.replace(/abc/g, (match) => `{${match}}`);
// "{abc}-xyz-{abc}"
'abc-xyz-abc'.replace(/abc/g, (match) => {});
// "undefined-xyz-undefined"
如果有捕获组,函数的后顺位参数与捕获组一一对应。
'@abc3-xyz-$abc5'.replace(/([^-]+)abc(\\d+)/g, (match, $1, $2) => `{${$1}${match}${$2}}`);
// <a href="mailto:{@@abc33}-xyz-{$$abc55}">{@@abc33}-xyz-{$$abc55}</a>
倒数第二个参数是匹配结果在文本中的位置。
'@abc-xyz-$abc'.replace(/([^-]+)abc/g, (match, $1, index) => `{${match}是位置是${index}}`);
// <a href="mailto:{@abc是位置是0}-xyz-{$abc是位置是9}">{@abc是位置是0}-xyz-{$abc是位置是9}</a>
倒数第一个参数是源文本。
'abc-xyz'.replace(/abc/g, (match, index, string) => `{{${match}}属于{${string}}}`);
// "{{abc}属于{abc-xyz}}-xyz"
replace方法最常用的地方是转义HTML标签。
'<p>hello regex</p>'.replace(/</g, '<').replace(/>/g, '>');
// "<p>hello regex</p>"

#### search

search 是String实例方法。
它的作用是找出首次匹配项的索引。它的功能较单一,性能也更好。
它接受一个正则表达式作为唯一参数。与match一样,如果传入一个非正则表达式,它会调用 new RegExp() 将其转换成一个正则实例。
>'abc-xyz-abc'.search(/xyz/);
>// 4
>'abc-xyz-abc'.search(/xyz/g);
>// 4
>'abc-xyz-abc'.search(/mno/);
>// -1
>'abc-xyz-abc'.search();
>// 0
>'abc-xyz-abc'.search(/abc/);
>// 0
因为只能返回首次匹配的位置,所以全局匹配对它无效。
如果匹配失败,返回 -1 。

#### split

split 是String实例方法。
它的作用是根据传入的分隔符切割源文本。它返回一个由被切割单元组成的数组。
它接受两个参数。第一个参数可以是字符串或者正则表达式,它是分隔符;第二个参数可选,限制返回数组的最大长度。
'abc-def_mno+xyz'.split();
// ["abc-def_mno+xyz"]
'abc-def_mno+xyz'.split('-_+');
// ["abc-def_mno+xyz"]
'abc-def_mno+xyz'.split('');
// ["a", "b", "c", "-", "d", "e", "f", "_", "m", "n", "o", "+", "x", "y", "z"]
'abc-def_mno+xyz'.split(/[-_+]/);
// ["abc", "def", "mno", "xyz"]
'abc-def_mno+xyz'.split(/[-_+]/g);
// ["abc", "def", "mno", "xyz"]
'abc-def_mno+xyz'.split(/[-_+]/, 3);
// ["abc", "def", "mno"]
'abc-def_mno+xyz'.split(/[-_+]/, 5);
// ["abc", "def", "mno", "xyz"]
如果第一个参数传入的是空字符串,则会切割每一个字符串。
另外,因为split方法中的正则是用来匹配分隔符,所以全局匹配没有意义。

#### 使用 test()

test() 方法是一个正则表达式方法。
test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。
以下实例用于搜索字符串中的字符 "e":
`var patt = /e/;`
`patt.test("The best things in life are free!");
`
字符串中含有 "e",所以该实例输出为:
>true

#### 使用 exec()

exec() 方法是一个正则表达式方法。
exec() 方法用于检索字符串中的正则表达式的匹配。
该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
以下实例用于搜索字符串中的字母 "e":
Example 1
`/e/.exec("The best things in life are free!");`
字符串中含有 "e",所以该实例输出为:
>e

验证数字的正则表达式集

验证数字:^[0-9]*$

验证n位的数字:^\\d{n}$

验证至少n位数字:^\\d{n,}$

验证m-n位的数字:^\\d{m,n}$

验证零和非零开头的数字:^(0|[1-9][0-9]*)$

验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$

验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$

验证非零的正整数:^\\+?[1-9][0-9]*$

验证非零的负整数:^\\-[1-9][0-9]*$

验证非负整数(正整数 + 0) ^\\d+$

验证非正整数(负整数 + 0) ^((-\\d+)|(0+))$

验证长度为3的字符:^.{3}$

验证由26个英文字母组成的字符串:^[A-Za-z]+$

验证由26个大写英文字母组成的字符串:^[A-Z]+$

验证由26个小写英文字母组成的字符串:^[a-z]+$

验证由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$

验证由数字、26个英文字母或者下划线组成的字符串:^\\w+$

验证用户密码:^[a-zA-Z]\\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。

验证是否含有 ^%&',;=?$\\" 等字符:[^%&',;=?$\\x22]+

验证汉字:^[\\u4e00-\\u9fa5],{0,}$

验证Email地址:/^([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$/

验证InternetURL:^http://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?$ ;^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$

验证电话号码:^(\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}$:--正确格式为:XXXX-XXXXXXX,XXXX-XXXXXXXX,XXX-XXXXXXX,XXX-XXXXXXXX,XXXXXXX,XXXXXXXX。

验证身份证号(15位或18位数字):^\\d{15}|\\d{}18$

验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12”

验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 正确格式为:01、09和1、31。

整数:^-?\\d+$

非负浮点数(正浮点数 + 0):^\\d+(\\.\\d+)?$

正浮点数 ^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$

非正浮点数(负浮点数 + 0) ^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$

负浮点数 ^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$

浮点数 ^(-?\\d+)(\\.\\d+)?$

标签:

提交需求或反馈

Demand feedback