golang splitting string by space but considering quoted section as a single part

7,890 views
Skip to first unread message

Wei Tang

unread,
Jul 10, 2014, 4:55:44 PM7/10/14
to golang-nuts
Hi

I'd like to split a string based on space, but also consider a substring in quotes as a single part:

For example:

str := " arg1 arg2 'hello world' "

using string.Fields, I got:

strings.Field(string) =  ["arg1", "arg2", "'hello", "world'"]

But, what I want is an array like  ["arg1", "arg2", "'hello world'"]

Any idea on how to meet my requirement easily (without parsing the string character by character)?

Thanks!
Wei

Michael Jones

unread,
Jul 10, 2014, 6:36:16 PM7/10/14
to Wei Tang, golang-nuts
Easily means what, with little typing?

while string not empty {
scan for a double quote.
extract the part before than and split on spaces 
scan for the closing double quote
extract this part and append it to the result of the split
reslice the string to be the remainder

play with the list, some of which will start and end with double quotes.

Maybe you could do it with less typing by using regular expressions to extract the two parts, the one before the quoted section and the one that is the quoted section, but that seems like using overly heavy duty equipment for a trivia task.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1 650-335-5765

Alex Skinner

unread,
Jul 10, 2014, 6:56:14 PM7/10/14
to golan...@googlegroups.com
You could use shellwords package which does the hard work for you...


func main() {
        z,_ := shellwords.Parse("arg1 arg2 'hello there'")
        for _,v := range z {
                fmt.Println(v)

Soheil Hassas Yeganeh

unread,
Jul 10, 2014, 7:08:33 PM7/10/14
to golan...@googlegroups.com
Or you can use a regex: '.+'|\".+\"|\\S+

http://play.golang.org/p/SYWa_wYpt9 That'd be slower though.

Siddon Tang

unread,
Jul 10, 2014, 9:23:23 PM7/10/14
to golan...@googlegroups.com

Amazing, I tried to find a package to do this hard work but found nothing, so I have been using regexp:  `'.*?'|".*?"|\S+`

Maybe I can use go-shellwords later.

在 2014年7月11日星期五UTC+8上午6时56分14秒,Alex Skinner写道:

mgutz

unread,
Jul 10, 2014, 11:04:27 PM7/10/14
to golan...@googlegroups.com
Added str.ToArgv() to github.com/mgutz/str library. Needed this for my watcher and converted what I had been using in Node.js. Please add tests to str_test.go if you find anything wrong. Thanks

Soheil Hassas Yeganeh

unread,
Jul 11, 2014, 9:04:43 AM7/11/14
to golan...@googlegroups.com
Or without loop/regex: http://play.golang.org/p/ztqfYiPSlv

AJ ONeal

unread,
Jun 1, 2019, 11:14:14 PM6/1/19
to golang-nuts
Based on the work of others here I created my own version that passes the tests I needed to pass:

* strips outer quotes
* keeps inner quotes
* empty quotes produce empty string


const NullStr = rune(0) 
 
// ParseArgs will parse a string that contains quoted strings the same as bash does
// (same as most other *nix shells do). This is secure in the sense that it doesn't do any
// executing or interpeting. However, it also doesn't do any escaping, so you shouldn't pass
// these strings to shells without escaping them.
func ParseArgs(str string) ([]string, error) {
var m []string
var s string 
 
str = strings.TrimSpace(str) + " "
lastQuote := NullStr
isSpace := false 
 
for i, c := range str {
switch {
// If we're ending a quote, break out and skip this character
case c == lastQuote:
lastQuote = NullStr 
 
// If we're in a quote, count this character
case lastQuote != NullStr:
s += string(c) 
 
// If we encounter a quote, enter it and skip this character
case unicode.In(c, unicode.Quotation_Mark):
isSpace = false
lastQuote = c 
 
// If it's a space, store the string
case unicode.IsSpace(c):
if 0 == i || isSpace {
continue
}
isSpace = true
m = append(m, s)
s = "" 
 
default:
isSpace = false
s += string(c)
}
}
if lastQuote != NullStr {
return nil, fmt.Errorf("Quotes did not terminate")
}
return m, nil
}

Lucio

unread,
Jun 2, 2019, 11:25:28 PM6/2/19
to golang-nuts
On Sunday, 2 June 2019 05:14:14 UTC+2, AJ ONeal wrote:
Based on the work of others here I created my own version that passes the tests I needed to pass:

* strips outer quotes
* keeps inner quotes
* empty quotes produce empty string
 
My immediate (knee-jerk?) reaction was "where can I find the tests?".

I confess I've become a bit of a test-bigot since I started appreciating the value of developing from the bottom-up (out of necessity, not preference).

Normally, I would keep such reactions to myself, but in this case I think there are others - not excluding the OP - who would benefit from the availability of a test suite.

Lucio.

Michael Fromberger

unread,
Jun 2, 2019, 11:35:24 PM6/2/19
to golang-nuts
You might find http://godoc.org/bitbucket.org/creachadair/shell useful also.

–M

Albert Li

unread,
Sep 21, 2023, 1:34:47 PM9/21/23
to golang-nuts
// SplitString split string with a rune comma ignore quoted
func SplitString(str string, r rune) []string {
quoted := false
return strings.FieldsFunc(str, func(r1 rune) bool {
if r1 == '\'' {
quoted = !quoted
}
return !quoted && r1 == r
})
}
// TestSpitString test case
func TestSpitString(t *testing.T) {
str := " arg1 arg2 'hello world' "
arr := SplitString(str, ' ')
fmt.Println(len(arr))
for i, line := range arr {
fmt.Println(i, "---", line)
}
}
Reply all
Reply to author
Forward
0 new messages