@@ -65,7 +65,40 @@ func allIndex(s string, substr string, reg *regexp.Regexp) [][]int {
6565 if reg != nil {
6666 return reg .FindAllStringIndex (s , - 1 )
6767 }
68- return allStringIndex (s , substr )
68+ return allDelimiterIndex (s , substr )
69+ }
70+
71+ // allDelimiterIndex returns all delimiter positions.
72+ // A delimiter inside a double-quoted field is part of the field, not a
73+ // delimiter, including when the quoted field is the first one on the line.
74+ func allDelimiterIndex (s string , substr string ) [][]int {
75+ if len (substr ) == 0 {
76+ return nil
77+ }
78+ var result [][]int
79+ width := len (substr )
80+ offSet := 0
81+ if strings .HasPrefix (s , `"` ) {
82+ s , offSet = skipQuoted (s , offSet )
83+ }
84+ for pos := strings .Index (s , substr ); pos != - 1 ; pos = strings .Index (s , substr ) {
85+ s = s [pos + width :]
86+ result = append (result , []int {pos + offSet , pos + offSet + width })
87+ offSet += pos + width
88+
89+ if strings .HasPrefix (s , `"` ) {
90+ s , offSet = skipQuoted (s , offSet )
91+ }
92+ }
93+ return result
94+ }
95+
96+ // skipQuoted skips the double-quoted field at the beginning of s and returns
97+ // the remainder with the updated offset. An unclosed quote skips only the
98+ // opening quote, which is what the delimiter search did before.
99+ func skipQuoted (s string , offSet int ) (string , int ) {
100+ qpos := strings .Index (s [1 :], `"` )
101+ return s [qpos + 2 :], offSet + qpos + 2
69102}
70103
71104// allStringIndex returns all matching string positions.
0 commit comments