替换除最后一次出现之外的所有字符。

huangapple go评论113阅读模式
英文:

Replace all characters except for the last occurrence

问题

我正在对字符串执行字符替换,像这样:

  1. result = strings.ReplaceAll(result, ".", "_")

这个操作按预期工作,但是我想保留最后一个出现的 .,不进行替换,只是保持原样。

有没有一种巧妙的方法可以实现这个需求?

英文:

I am performing a replace of characters on a string like this:

  1. 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,然后将除最后一个元素外的所有元素使用指定的分隔符连接起来。

  1. func ReplaceAllButLast(data, old, new string) string {
  2. split := strings.Split(data, old)
  3. if len(split) < 3 {
  4. return data
  5. }
  6. last := len(split) - 1
  7. return strings.Join(split[:last], new) + old + split[last]
  8. }

链接:https://go.dev/play/p/j8JJP-p_Abk

  1. func main() {
  2. println(ReplaceAllButLast("a", ".", "_"))
  3. println(ReplaceAllButLast("a.b", ".", "_"))
  4. println(ReplaceAllButLast("a.b.c", ".", "_"))
  5. println(ReplaceAllButLast("a.b.c.d", ".", "_"))
  6. }

输出结果:

  1. a
  2. a.b
  3. a_b.c
  4. a_b_c.d

更新:

这只是一个玩笑,只是为了装

最好的方法是计算匹配的次数,然后替换Count()-1,第二种方法是在最后一个匹配位置之前使用ReplaceAll,并使用Join是最慢的。

  1. func ReplaceAllButLast_Count(data, old, new string) string {
  2. cnt := strings.Count(data, old)
  3. if cnt < 2 {
  4. return data
  5. }
  6. return strings.Replace(data, old, new, cnt-1)
  7. }
  8. func ReplaceAllButLast_Replace(data, old, new string) string {
  9. idx := strings.LastIndex(data, old)
  10. if idx <= 0 {
  11. return data
  12. }
  13. return strings.ReplaceAll(data[:idx], old, new) + data[idx:]
  14. }
  15. func ReplaceAllButLast_Join(data, old, new string) string {
  16. split := strings.Split(data, old)
  17. if len(split) < 3 {
  18. return data
  19. }
  20. last := len(split) - 1
  21. return strings.Join(split[:last], new) + old + split[last]
  22. }

使用a.b.c.d -&gt; a_b_c.d进行基准测试。

  1. goos: windows
  2. goarch: amd64
  3. pkg: example.org
  4. cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
  5. Benchmark_Count-8 16375098 70.05 ns/op 8 B/op 1 allocs/op
  6. Benchmark_Replace-8 11213830 108.5 ns/op 16 B/op 2 allocs/op
  7. Benchmark_Slice-8 5460445 217.6 ns/op 80 B/op 3 allocs/op
英文:

Split into []string, then Join all but last 替换除最后一次出现之外的所有字符。

  1. func ReplaceAllButLast(data, old, new string) string {
  2. split := strings.Split(data, old)
  3. if len(split) &lt; 3 {
  4. return data
  5. }
  6. last := len(split) - 1
  7. return strings.Join(split[:last], new) + old + split[last]
  8. }

https://go.dev/play/p/j8JJP-p_Abk

  1. func main() {
  2. println(ReplaceAllButLast(&quot;a&quot;, &quot;.&quot;, &quot;_&quot;))
  3. println(ReplaceAllButLast(&quot;a.b&quot;, &quot;.&quot;, &quot;_&quot;))
  4. println(ReplaceAllButLast(&quot;a.b.c&quot;, &quot;.&quot;, &quot;_&quot;))
  5. println(ReplaceAllButLast(&quot;a.b.c.d&quot;, &quot;.&quot;, &quot;_&quot;))
  6. }

produced

  1. a
  2. a.b
  3. a_b.c
  4. 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

  1. func ReplaceAllButLast_Count(data, old, new string) string {
  2. cnt := strings.Count(data, old)
  3. if cnt &lt; 2 {
  4. return data
  5. }
  6. return strings.Replace(data, old, new, cnt-1)
  7. }
  8. func ReplaceAllButLast_Replace(data, old, new string) string {
  9. idx := strings.LastIndex(data, old)
  10. if idx &lt;= 0 {
  11. return data
  12. }
  13. return strings.ReplaceAll(data[:idx], old, new) + data[idx:]
  14. }
  15. func ReplaceAllButLast_Join(data, old, new string) string {
  16. split := strings.Split(data, old)
  17. if len(split) &lt; 3 {
  18. return data
  19. }
  20. last := len(split) - 1
  21. return strings.Join(split[:last], new) + old + split[last]
  22. }

Benchmark using a.b.c.d -&gt; a_b_c.d

  1. goos: windows
  2. goarch: amd64
  3. pkg: example.org
  4. cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
  5. Benchmark_Count-8 16375098 70.05 ns/op 8 B/op 1 allocs/op
  6. Benchmark_Replace-8 11213830 108.5 ns/op 16 B/op 2 allocs/op
  7. Benchmark_Slice-8 5460445 217.6 ns/op 80 B/op 3 allocs/op

答案2

得分: 2

处理这个问题最明显的方法是结合使用ReplaceCount函数:

  1. func ReplaceAllExceptLast(d string, o string, n string) string {
  2. return strings.Replace(d, o, n, strings.Count(d, o)-1)
  3. }

然而,我认为这不是最优解。对我来说,最好的选择是这样做:

  1. func ReplaceAllExceptLast(d string, o string, n string) string {
  2. ln := strings.LastIndex(d, o)
  3. if ln == -1 {
  4. return d
  5. }
  6. return strings.ReplaceAll(d[:ln], o, n) + d[ln:]
  7. }

这个方法通过获取要替换的值的最后一次出现的索引,然后对该索引之前的字符串进行替换。例如:

  1. println(ReplaceAllExceptLast("a", ".", "_"))
  2. println(ReplaceAllExceptLast("a.b", ".", "_"))
  3. println(ReplaceAllExceptLast("a.b.c", ".", "_"))
  4. println(ReplaceAllExceptLast("a.b.c.d", ".", "_"))

将输出:

  1. a
  2. a.b
  3. a_b.c
  4. a_b_c.d
英文:

The most obvious way to handle this would be to combine Replace and Count:

  1. func ReplaceAllExceptLast(d string, o string, n string) string {
  2. strings.Replace(d, o, n, strings.Count(d, o) - 1)
  3. }

I don't think this would be the most optimal solution, however. For me, the best option would be to do this:

  1. func ReplaceAllExceptLast(d string, o string, n string) string {
  2. ln := strings.LastIndex(d, o)
  3. if ln == -1 {
  4. return d
  5. }
  6. return strings.ReplaceAll(d[:ln], o, n) + d[ln:]
  7. }

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:

  1. println(ReplaceAllExceptLast(&quot;a&quot;, &quot;.&quot;, &quot;_&quot;))
  2. println(ReplaceAllExceptLast(&quot;a.b&quot;, &quot;.&quot;, &quot;_&quot;))
  3. println(ReplaceAllExceptLast(&quot;a.b.c&quot;, &quot;.&quot;, &quot;_&quot;))
  4. println(ReplaceAllExceptLast(&quot;a.b.c.d&quot;, &quot;.&quot;, &quot;_&quot;))

will produce:

  1. a
  2. a.b
  3. a_b.c
  4. a_b_c.d

答案3

得分: 1

我来为你翻译一下:

我脑海中首先想到的解决方案是使用正则表达式的"正向先行断言"(Positive Lookahead)。

[.](?=.*[.])

然而,在Golang的re2库中不支持(?=re)这种语法。你可以参考re2的语法


我们可以使用"非捕获组"(Non-capturing group)来实现这个功能:

  • 首先使用(?:[.])来找到所有点的索引。

  • 然后将除了最后一个点之外的点替换为下划线。

示例:

  1. func replaceStringByIndex(str string, replacement string, index int) string {
  2. return str[:index] + replacement + str[index+1:]
  3. }
  4. func replaceDotIgnoreLast(str string, replacement string) string {
  5. pattern, err := regexp.Compile("(?:[.])")
  6. if err != nil {
  7. return ""
  8. }
  9. submatches := pattern.FindAllStringSubmatchIndex(str, -1)
  10. for _, submatch := range submatches[:len(submatches)-1] {
  11. str = replaceStringByIndex(str, replacement, submatch[0])
  12. }
  13. return str
  14. }
  15. func main() {
  16. str := "1.2"
  17. fmt.Println(replaceDotIgnoreLast(str, "_"))
  18. str = "1.2.3"
  19. fmt.Println(replaceDotIgnoreLast(str, "_"))
  20. str = "1.2.3.4"
  21. fmt.Println(replaceDotIgnoreLast(str, "_"))
  22. }

结果:

  1. 1.2
  2. 1_2.3
  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

  1. func replaceStringByIndex(str string, replacement string, index int) string {
  2. return str[:index] + replacement + str[index+1:]
  3. }
  4. func replaceDotIgnoreLast(str string, replacement string) string {
  5. pattern, err := regexp.Compile(&quot;(?:[.])&quot;)
  6. if err != nil {
  7. return &quot;&quot;
  8. }
  9. submatches := pattern.FindAllStringSubmatchIndex(str, -1)
  10. for _, submatch := range submatches[:len(submatches)-1] {
  11. str = replaceStringByIndex(str, replacement, submatch[0])
  12. }
  13. return str
  14. }
  15. func main() {
  16. str := &quot;1.2&quot;
  17. fmt.Println(replaceDotIgnoreLast(str, &quot;_&quot;))
  18. str = &quot;1.2.3&quot;
  19. fmt.Println(replaceDotIgnoreLast(str, &quot;_&quot;))
  20. str = &quot;1.2.3.4&quot;
  21. fmt.Println(replaceDotIgnoreLast(str, &quot;_&quot;))
  22. }

Result

  1. 1.2
  2. 1_2.3
  3. 1_2_3.4

答案4

得分: 0

  1. 使用strings.Count来计算要替换的字符串出现的次数
  2. 将count-1传递给strings.Replace以省略最后一次出现
  1. func ReplaceAllButLast(input string, find string, replace string) string {
  2. occurrencesCount := strings.Count(input, find)
  3. return strings.Replace(input, find, replace, occurrencesCount-1)
  4. }
英文:
  1. Use strings.Count to count the occurrence of the string you want to replace
  2. Pass count-1 to strings.Replace to omit the last occurrence
  1. func ReplaceAllButLast(input string, find string, replace string) string {
  2. occurrencesCount := strings.Count(input, find)
  3. return strings.Replace(input, find, replace, occurrencesCount-1)
  4. }

huangapple
  • 本文由 发表于 2022年9月2日 08:20:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/73576589.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定