lib/smarter_csv.rb in smarter_csv-1.8.4 vs lib/smarter_csv.rb in smarter_csv-1.8.5

- old
+ new

@@ -67,12 +67,12 @@ # cater for the quoted csv data containing the row separator carriage return character # in which case the row data will be split across multiple lines (see the sample content in spec/fixtures/carriage_returns_rn.csv) # by detecting the existence of an uneven number of quote characters - multiline = line.count(options[:quote_char]).odd? # should handle quote_char nil - while line.count(options[:quote_char]).odd? # should handle quote_char nil + multiline = count_quote_chars(line, options[:quote_char]).odd? # should handle quote_char nil + while count_quote_chars(line, options[:quote_char]).odd? # should handle quote_char nil next_line = fh.readline(options[:row_sep]) next_line = next_line.force_encoding('utf-8').encode('utf-8', invalid: :replace, undef: :replace, replace: options[:invalid_byte_sequence]) if options[:force_utf8] || options[:file_encoding] !~ /utf-8/i line += next_line @file_line_count += 1 end @@ -194,10 +194,25 @@ def headers @headers end + # Counts the number of quote characters in a line, excluding escaped quotes. + def count_quote_chars(line, quote_char) + return 0 if line.nil? || quote_char.nil? + + count = 0 + previous_char = '' + + line.each_char do |char| + count += 1 if char == quote_char && previous_char != '\\' + previous_char = char + end + + count + end + protected # NOTE: this is not called when "parse" methods are tested by themselves def default_options { @@ -308,18 +323,21 @@ quote_count = 0 elements = [] start = 0 i = 0 + previous_char = '' while i < line_size if line[i...i+col_sep_size] == col_sep && quote_count.even? break if !header_size.nil? && elements.size >= header_size elements << cleanup_quotes(line[start...i], quote) + previous_char = line[i] i += col_sep.size start = i else - quote_count += 1 if line[i] == quote + quote_count += 1 if line[i] == quote && previous_char != '\\' + previous_char = line[i] i += 1 end end elements << cleanup_quotes(line[start..-1], quote) if header_size.nil? || elements.size < header_size [elements, elements.size]