在引号内部的空格除外,将字符串拆分为子字符串

16

我想知道是否有一种简单的方法可以在空格处分割字符串,但忽略引号中的空格?

例如,将

Foo bar random "letters lol" stuff

转换为

Foobarrandom"letters lol"stuff

3个回答

18

想象一下,你有一个以逗号分隔值(CSV)文件格式存储的字符串,RFC4180,但是除了引号对之外,你的分隔符是空格(而不是逗号)。例如:

package main

import (
    "encoding/csv"
    "fmt"
    "strings"
)

func main() {
    s := `Foo bar random "letters lol" stuff`
    fmt.Printf("String:\n%q\n", s)

    // Split string
    r := csv.NewReader(strings.NewReader(s))
    r.Comma = ' ' // space
    fields, err := r.Read()
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("\nFields:\n")
    for _, field := range fields {
        fmt.Printf("%q\n", field)
    }
}

Playground: https://play.golang.org/p/Ed4IV97L7H

输出:

String:
"Foo bar random \"letters lol\" stuff"

Fields:
"Foo"
"bar"
"random"
"letters lol"
"stuff"

这不是我真正想要的输出。例如,我希望 letters lol 用引号括起来,而其他内容则不用。此外,我的问题表述得不够清楚...... 我原本想将 blockdata 8539 58 584 {CustomName:"Foo"} 拆分成 blockdata853958584{CustomName:"Foo"}... 应该在问题中提到这一点。 - MOBlox

14
  1. 使用 strings.FieldsFunc 尝试 这个
package main

import (
    "fmt"
    "strings"
)

func main() {
    s := `Foo bar random "letters lol" stuff`
    quoted := false
    a := strings.FieldsFunc(s, func(r rune) bool {
        if r == '"' {
            quoted = !quoted
        }
        return !quoted && r == ' '
    })

    out := strings.Join(a, ", ")
    fmt.Println(out) // Foo, bar, random, "letters lol", stuff
}

  1. 使用简单的 strings.Builderrange 遍历字符串,根据需要保留或不保留 ",尝试 this
package main

import (
    "fmt"
    "strings"
)

func main() {
    s := `Foo bar random "letters lol" stuff`
    a := []string{}
    sb := &strings.Builder{}
    quoted := false
    for _, r := range s {
        if r == '"' {
            quoted = !quoted
            sb.WriteRune(r) // keep '"' otherwise comment this line
        } else if !quoted && r == ' ' {
            a = append(a, sb.String())
            sb.Reset()
        } else {
            sb.WriteRune(r)
        }
    }
    if sb.Len() > 0 {
        a = append(a, sb.String())
    }

    out := strings.Join(a, ", ")
    fmt.Println(out) // Foo, bar, random, "letters lol", stuff
    // not keep '"': // Foo, bar, random, letters lol, stuff
}


  1. 使用 scanner.Scanner,尝试 这个
package main

import (
    "fmt"
    "strings"
    "text/scanner"
)

func main() {
    var s scanner.Scanner
    s.Init(strings.NewReader(`Foo bar random "letters lol" stuff`))
    slice := make([]string, 0, 5)
    tok := s.Scan()
    for tok != scanner.EOF {
        slice = append(slice, s.TokenText())
        tok = s.Scan()
    }
    out := strings.Join(slice, ", ")
    fmt.Println(out) // Foo, bar, random, "letters lol", stuff
}

  1. 使用 csv.NewReader,它会自动删除 ",请尝试 这样做
package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "strings"
)

func main() {
    s := `Foo bar random "letters lol" stuff`
    r := csv.NewReader(strings.NewReader(s))
    r.Comma = ' '
    record, err := r.Read()
    if err != nil {
        log.Fatal(err)
    }

    out := strings.Join(record, ", ")
    fmt.Println(out) // Foo, bar, random, letters lol, stuff
}

使用 regexp,尝试 this
package main

import (
    "fmt"
    "regexp"
    "strings"
)

func main() {
    s := `Foo bar random "letters lol" stuff`

    r := regexp.MustCompile(`[^\s"]+|"([^"]*)"`)
    a := r.FindAllString(s, -1)

    out := strings.Join(a, ", ")
    fmt.Println(out) // Foo, bar, random, "letters lol", stuff
}

在文档中,FieldsFunc对此发出了警告,这使得你的第一个建议有些危险:"FieldsFunc对于调用f(c)的顺序不做任何保证,并且假设对于给定的c,f始终返回相同的值。" - undefined
@BenMoss 我明白了,这有点奇怪!我认为这与Go编程语言的特点不符,因为它本应该没有魔法:但是直到今天的Go 1.21.3版本,循环 for end, rune := range s { if f(rune) { 的确没有魔法,它按照输入字符串的符文顺序进行操作。希望没有人决定在其中施展魔法!否则我们就需要复制 FieldsFunc 的副本了! - undefined

2
您可以使用正则表达式
这个(go playground)将覆盖所有引号内多个词和数组中多个引用条目的用例。
package main

import (
    "fmt"
    "regexp"
)

func main() {
    s := `Foo bar random "letters lol" stuff "also will" work on "multiple quoted stuff"`       
    r := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)`) 
    arr := r.FindAllString(s, -1)       
    fmt.Println("your array: ", arr)    
}

输出将是:
[Foo, bar, random, "letters lol", stuff, "also will", work, on, "multiple quoted stuff"]

如果你想了解更多关于正则表达式的知识,这里有一个很棒的SO答案,在末尾提供了非常方便的资源 - 学习正则表达式。希望这可以帮到你。

似乎你只需要:[^\s"]+|"([^"]*)". 我猜意图是处理单引号,但测试中没有(并且由于末尾缺少'而无法工作)。 - Brent Bradburn

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