英文:
Replace all characters except for the last occurrence
问题
我正在对字符串执行字符替换,像这样:
result = strings.ReplaceAll(result, ".", "_")
这个操作按预期工作,但是我想保留最后一个出现的 .
,不进行替换,只是保持原样。
有没有一种巧妙的方法可以实现这个需求?
英文:
I am performing a replace of characters on a string like this:
result = strings.ReplaceAll(result, ".", "_")
This works as expected, but I want to preserve the last occurence of the .
and not replace it, just leave it alone.
Is there a fancy way doing this?
答案1
得分: 2
将字符串拆分为[]string
,然后将除最后一个元素外的所有元素使用指定的分隔符连接起来。
func ReplaceAllButLast(data, old, new string) string {
split := strings.Split(data, old)
if len(split) < 3 {
return data
}
last := len(split) - 1
return strings.Join(split[:last], new) + old + split[last]
}
链接:https://go.dev/play/p/j8JJP-p_Abk
func main() {
println(ReplaceAllButLast("a", ".", "_"))
println(ReplaceAllButLast("a.b", ".", "_"))
println(ReplaceAllButLast("a.b.c", ".", "_"))
println(ReplaceAllButLast("a.b.c.d", ".", "_"))
}
输出结果:
a
a.b
a_b.c
a_b_c.d
更新:
这只是一个玩笑,只是为了装逼。
最好的方法是计算匹配的次数,然后替换Count()-1
,第二种方法是在最后一个匹配位置之前使用ReplaceAll
,并使用Join
是最慢的。
func ReplaceAllButLast_Count(data, old, new string) string {
cnt := strings.Count(data, old)
if cnt < 2 {
return data
}
return strings.Replace(data, old, new, cnt-1)
}
func ReplaceAllButLast_Replace(data, old, new string) string {
idx := strings.LastIndex(data, old)
if idx <= 0 {
return data
}
return strings.ReplaceAll(data[:idx], old, new) + data[idx:]
}
func ReplaceAllButLast_Join(data, old, new string) string {
split := strings.Split(data, old)
if len(split) < 3 {
return data
}
last := len(split) - 1
return strings.Join(split[:last], new) + old + split[last]
}
使用a.b.c.d -> a_b_c.d
进行基准测试。
goos: windows
goarch: amd64
pkg: example.org
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
Benchmark_Count-8 16375098 70.05 ns/op 8 B/op 1 allocs/op
Benchmark_Replace-8 11213830 108.5 ns/op 16 B/op 2 allocs/op
Benchmark_Slice-8 5460445 217.6 ns/op 80 B/op 3 allocs/op
英文:
Split into []string
, then Join
all but last
func ReplaceAllButLast(data, old, new string) string {
split := strings.Split(data, old)
if len(split) < 3 {
return data
}
last := len(split) - 1
return strings.Join(split[:last], new) + old + split[last]
}
https://go.dev/play/p/j8JJP-p_Abk
func main() {
println(ReplaceAllButLast("a", ".", "_"))
println(ReplaceAllButLast("a.b", ".", "_"))
println(ReplaceAllButLast("a.b.c", ".", "_"))
println(ReplaceAllButLast("a.b.c.d", ".", "_"))
}
produced
a
a.b
a_b.c
a_b_c.d
UPDATE
That was a joke, just to be fancy
Best way is to count the number of matches and replace Count()-1
, the second it to ReplaceAll
until last match position and using Join
to be the slowest
func ReplaceAllButLast_Count(data, old, new string) string {
cnt := strings.Count(data, old)
if cnt < 2 {
return data
}
return strings.Replace(data, old, new, cnt-1)
}
func ReplaceAllButLast_Replace(data, old, new string) string {
idx := strings.LastIndex(data, old)
if idx <= 0 {
return data
}
return strings.ReplaceAll(data[:idx], old, new) + data[idx:]
}
func ReplaceAllButLast_Join(data, old, new string) string {
split := strings.Split(data, old)
if len(split) < 3 {
return data
}
last := len(split) - 1
return strings.Join(split[:last], new) + old + split[last]
}
Benchmark using a.b.c.d -> a_b_c.d
goos: windows
goarch: amd64
pkg: example.org
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
Benchmark_Count-8 16375098 70.05 ns/op 8 B/op 1 allocs/op
Benchmark_Replace-8 11213830 108.5 ns/op 16 B/op 2 allocs/op
Benchmark_Slice-8 5460445 217.6 ns/op 80 B/op 3 allocs/op
答案2
得分: 2
处理这个问题最明显的方法是结合使用Replace
和Count
函数:
func ReplaceAllExceptLast(d string, o string, n string) string {
return strings.Replace(d, o, n, strings.Count(d, o)-1)
}
然而,我认为这不是最优解。对我来说,最好的选择是这样做:
func ReplaceAllExceptLast(d string, o string, n string) string {
ln := strings.LastIndex(d, o)
if ln == -1 {
return d
}
return strings.ReplaceAll(d[:ln], o, n) + d[ln:]
}
这个方法通过获取要替换的值的最后一次出现的索引,然后对该索引之前的字符串进行替换。例如:
println(ReplaceAllExceptLast("a", ".", "_"))
println(ReplaceAllExceptLast("a.b", ".", "_"))
println(ReplaceAllExceptLast("a.b.c", ".", "_"))
println(ReplaceAllExceptLast("a.b.c.d", ".", "_"))
将输出:
a
a.b
a_b.c
a_b_c.d
英文:
The most obvious way to handle this would be to combine Replace
and Count
:
func ReplaceAllExceptLast(d string, o string, n string) string {
strings.Replace(d, o, n, strings.Count(d, o) - 1)
}
I don't think this would be the most optimal solution, however. For me, the best option would be to do this:
func ReplaceAllExceptLast(d string, o string, n string) string {
ln := strings.LastIndex(d, o)
if ln == -1 {
return d
}
return strings.ReplaceAll(d[:ln], o, n) + d[ln:]
}
This works by getting the index of the last occurrence of the value you want to replace, and then doing a replace-all on the string up to that point. For example:
println(ReplaceAllExceptLast("a", ".", "_"))
println(ReplaceAllExceptLast("a.b", ".", "_"))
println(ReplaceAllExceptLast("a.b.c", ".", "_"))
println(ReplaceAllExceptLast("a.b.c.d", ".", "_"))
will produce:
a
a.b
a_b.c
a_b_c.d
答案3
得分: 1
我来为你翻译一下:
我脑海中首先想到的解决方案是使用正则表达式的"正向先行断言"(Positive Lookahead)。
[.](?=.*[.])
然而,在Golang的re2库中不支持(?=re)
这种语法。你可以参考re2的语法。
我们可以使用"非捕获组"(Non-capturing group)来实现这个功能:
-
首先使用
(?:[.])
来找到所有点的索引。 -
然后将除了最后一个点之外的点替换为下划线。
示例:
func replaceStringByIndex(str string, replacement string, index int) string {
return str[:index] + replacement + str[index+1:]
}
func replaceDotIgnoreLast(str string, replacement string) string {
pattern, err := regexp.Compile("(?:[.])")
if err != nil {
return ""
}
submatches := pattern.FindAllStringSubmatchIndex(str, -1)
for _, submatch := range submatches[:len(submatches)-1] {
str = replaceStringByIndex(str, replacement, submatch[0])
}
return str
}
func main() {
str := "1.2"
fmt.Println(replaceDotIgnoreLast(str, "_"))
str = "1.2.3"
fmt.Println(replaceDotIgnoreLast(str, "_"))
str = "1.2.3.4"
fmt.Println(replaceDotIgnoreLast(str, "_"))
}
结果:
1.2
1_2.3
1_2_3.4
英文:
The first solution comes to my mind is Positive Lookahead
of regular expression
[.](?=.*[.])
However, the (?=re)
is not supported in Golang re2
We can use Non-capturing group
to implement it in this way
-
First use
(?:[.])
( Match a single character present in the list below [.]) to find all dot indexes. -
Then replace dot with '_' except the last one.
Sample
func replaceStringByIndex(str string, replacement string, index int) string {
return str[:index] + replacement + str[index+1:]
}
func replaceDotIgnoreLast(str string, replacement string) string {
pattern, err := regexp.Compile("(?:[.])")
if err != nil {
return ""
}
submatches := pattern.FindAllStringSubmatchIndex(str, -1)
for _, submatch := range submatches[:len(submatches)-1] {
str = replaceStringByIndex(str, replacement, submatch[0])
}
return str
}
func main() {
str := "1.2"
fmt.Println(replaceDotIgnoreLast(str, "_"))
str = "1.2.3"
fmt.Println(replaceDotIgnoreLast(str, "_"))
str = "1.2.3.4"
fmt.Println(replaceDotIgnoreLast(str, "_"))
}
Result
1.2
1_2.3
1_2_3.4
答案4
得分: 0
- 使用strings.Count来计算要替换的字符串出现的次数
- 将count-1传递给strings.Replace以省略最后一次出现
func ReplaceAllButLast(input string, find string, replace string) string {
occurrencesCount := strings.Count(input, find)
return strings.Replace(input, find, replace, occurrencesCount-1)
}
英文:
- Use strings.Count to count the occurrence of the string you want to replace
- Pass count-1 to strings.Replace to omit the last occurrence
func ReplaceAllButLast(input string, find string, replace string) string {
occurrencesCount := strings.Count(input, find)
return strings.Replace(input, find, replace, occurrencesCount-1)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论