docs: public boolean readColumns(final List<String> columns) throws IOException {
  		
  		if( columns == null ) {
  			throw new NullPointerException("columns should not be null");
  		}
  		
  		// clear the reusable List and StringBuilders
  		columns.clear();
  		currentColumn.setLength(0);
  		currentRow.setLength(0);
  		
  		// read a line (ignoring empty lines/comments if necessary)
  		String line;
  		do {
  			line = readLine();
  			if( line == null ) {
  				return false; // EOF
  			}
  		}
  		while( ignoreEmptyLines && line.length() == 0 || (commentMatcher != null && commentMatcher.isComment(line)) );
  		
  		// update the untokenized CSV row
  		currentRow.append(line);
  		
  		// process each character in the line, catering for surrounding quotes (QUOTE_MODE)
  		TokenizerState state = TokenizerState.NORMAL;
  		int quoteScopeStartingLine = -1; // the line number where a potential multi-line cell starts
  		int potentialSpaces = 0; // keep track of spaces (so leading/trailing space can be removed if required)
  		int charIndex = 0;
  		while( true ) {
  			boolean endOfLineReached = charIndex == line.length();
  			
  			if( endOfLineReached )
  			{
  				if( TokenizerState.NORMAL.equals(state) ) {
  					/*
  					 * Newline. Add any required spaces (if surrounding spaces don't need quotes) and return (we've read
  					 * a line!).
  					 */
  					if( !surroundingSpacesNeedQuotes ) {
  						appendSpaces(currentColumn, potentialSpaces);
  					}
  					addColumn(columns, line, charIndex);
  					return true;
  				}
  				else
  				{
  					/*
  					 * Newline. Doesn't count as newline while in QUOTESCOPE. Add the newline char, reset the charIndex
  					 * (will update to 0 for next iteration), read in the next line, then then continue to next
  					 * character.
  					 */
  					currentColumn.append(NEWLINE);
  					currentRow.append(NEWLINE); // specific line terminator lost, \n will have to suffice
  					
  					charIndex = 0;
  
  					if (maxLinesPerRow > 0 && getLineNumber() - quoteScopeStartingLine + 1 >= maxLinesPerRow) {
  						/*
  						 * The quoted section that is being parsed spans too many lines, so to avoid excessive memory
  						 * usage parsing something that is probably human error anyways, throw an exception. If each
  						 * row is suppose to be a single line and this has been exceeded, throw a more descriptive
  						 * exception
  						 */
  						String msg = maxLinesPerRow == 1 ?
  								String.format("unexpected end of line while reading quoted column on line %d",
  											  getLineNumber()) :
  								String.format("max number of lines to read exceeded while reading quoted column" +
  											  " beginning on line %d and ending on line %d",
  											  quoteScopeStartingLine, getLineNumber());
  						throw new SuperCsvException(msg);
  					}
  					else if( (line = readLine()) == null ) {
  						throw new SuperCsvException(
  							String
  								.format(
  									"unexpected end of file while reading quoted column beginning on line %d and ending on line %d",
  									quoteScopeStartingLine, getLineNumber()));
  					}
  					
  					currentRow.append(line); // update untokenized CSV row
  					
  				    if (line.length() == 0){
  				    	// consecutive newlines
                          continue;
  				    }
  				}
  			}
  			
  			final char c = line.charAt(charIndex);
  			
  			if( TokenizerState.NORMAL.equals(state) ) {
  				
  				/*
  				 * NORMAL mode (not within quotes).
  				 */
  				
  				if( c == delimiterChar) {
  					/*
  					 * Delimiter. Save the column (trim trailing space if required) then continue to next character.
  					 */
  					if( !surroundingSpacesNeedQuotes ) {
  						appendSpaces(currentColumn, potentialSpaces);
  					}
  					addColumn(columns, line, charIndex);
  					potentialSpaces = 0;
  					currentColumn.setLength(0);
  					
  				} else if( c == SPACE ) {
  					/*
  					 * Space. Remember it, then continue to next character.
  					 */
  					potentialSpaces++;
  					
  				}
  				else if( c == quoteChar ) {
  					/*
  					 * A single quote ("). Update to QUOTESCOPE (but don't save quote), then continue to next character.
  					 */
  					state = TokenizerState.QUOTE_MODE;
  					quoteScopeStartingLine = getLineNumber();
  					
  					// cater for spaces before a quoted section (be lenient!)
  					if( !surroundingSpacesNeedQuotes || currentColumn.length() > 0 ) {
  						appendSpaces(currentColumn, potentialSpaces);
  					}
  					potentialSpaces = 0;
  					
  				} else {
  					/*
  					 * Just a normal character. Add any required spaces (but trim any leading spaces if surrounding
  					 * spaces need quotes), add the character, then continue to next character.
  					 */
  					if( !surroundingSpacesNeedQuotes || currentColumn.length() > 0 ) {
  						appendSpaces(currentColumn, potentialSpaces);
  					}
  					
  					potentialSpaces = 0;
  					currentColumn.append(c);
  				}
  
  			} else {
  
  				/*
  				 * QUOTE_MODE (within quotes).
  				 */
  
  				if( c == quoteEscapeChar ) {
  					int nextCharIndex = charIndex + 1;
  					boolean availableCharacters = nextCharIndex < line.length();
  					boolean nextCharIsQuote = availableCharacters && line.charAt(nextCharIndex) == quoteChar;
  					boolean nextCharIsEscapeQuoteChar = availableCharacters && line.charAt(nextCharIndex) == quoteEscapeChar;
  
  					if( nextCharIsQuote ) {
  						/*
  						 * An escaped quote (e.g. "" or \"). Skip over the escape char, and add
  						 * the following quote char as part of the column;
  						 */
  						charIndex++;
  						currentColumn.append(quoteChar);
  					} else if( nextCharIsEscapeQuoteChar ) {
  						/*
  						 * A double escape (normally \\). Save the escape char, then continue to
  						 * next character.
  						 */
  						currentColumn.append(c);
  						charIndex++;
  					} else if( quoteEscapeChar == quoteChar ) {
  						/*
  						 * If the escape char is also the quote char and we didn't escape a
  						 * subsequent character, then this is a lone quote and the end of the
  						 * field.
  						 */
  						state = TokenizerState.NORMAL;
  						quoteScopeStartingLine = -1; // reset ready for next multi-line cell
  					} else {
  						/*
  						 * Escape char wasn't before either another escape char or a quote char,
  						 * so process it normally.
  						 */
  						currentColumn.append(c);
  					}
  				} else if( c == quoteChar ) {
  
  					/*
  					 * A single quote ("). Update to NORMAL (but don't save quote), then continue to next character.
  					 */
  					state = TokenizerState.NORMAL;
  					quoteScopeStartingLine = -1; // reset ready for next multi-line cell
  
  					int nextCharIndex = charIndex + 1;
  					boolean availableCharacters = nextCharIndex < line.length();
  					boolean nextCharIsQuote = availableCharacters && line.charAt(nextCharIndex) == quoteChar;
  
  					if( quoteEscapeChar != quoteChar && nextCharIsQuote ) {
  						throw new SuperCsvException("Encountered repeat quote char (" +
  								quoteChar + ") when quoteEscapeChar was (" + quoteEscapeChar + ")" +
  								".  Cannot process data where quotes are escaped both with " +
  								quoteChar + " and with " + quoteEscapeChar);
  					}
  				} else {
  					/*
  					 * Just a normal character, delimiter (they don't count in QUOTESCOPE) or space. Add the character,
  					 * then continue to next character.
  					 */
  					currentColumn.append(c);
  				}
  			}
  			
  			charIndex++; // read next char of the line
  		}
  	}