@@ -215,12 +215,18 @@ def _guess_quote_and_delimiter(self, data, delimiters):
215215 this way.
216216 """
217217
218+ # The body of a quoted field ends at the first quote which is
219+ # not doubled, as it does for a reader. A lazy ".*?" scans to
220+ # the end of the sample instead, from every start: quadratically.
221+ # As an unrolled loop it is unambiguous, so it does not backtrack.
222+ other = r'(?:(?!(?P=quote)).)*'
223+ body = r'%s(?:(?P=quote){2}%s)*' % (other , other )
218224 matches = []
219- for restr in (r'(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*? (?P=quote)(?P=delim)' , # ,".*? ",
220- r'(?:^|\n)(?P<quote>["\']).*? (?P=quote)(?P<delim>[^\w\n"\'])(?P<space> ?)' , # ".*? ",
221- r'(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*? (?P=quote)(?:$|\n)' , # ,".*? "
222- r'(?:^|\n)(?P<quote>["\']).*? (?P=quote)(?:$|\n)' ): # ".*? " (no delim, no space)
223- regexp = re .compile (restr , re .DOTALL | re .MULTILINE )
225+ for restr in (r'(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\'])%s (?P=quote)(?P=delim)' , # ,"... ",
226+ r'(?:^|\n)(?P<quote>["\'])%s (?P=quote)(?P<delim>[^\w\n"\'])(?P<space> ?)' , # "... ",
227+ r'(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\'])%s (?P=quote)(?:$|\n)' , # ,"... "
228+ r'(?:^|\n)(?P<quote>["\'])%s (?P=quote)(?:$|\n)' ): # "... " (no delim, no space)
229+ regexp = re .compile (restr % body , re .DOTALL | re .MULTILINE )
224230 matches = regexp .findall (data )
225231 if matches :
226232 break
0 commit comments