Java/Development Class/CSV File

Материал из Java эксперт
Перейти к: навигация, поиск

A stream based parser for parsing delimited text data from a file or a stream

   <source lang="java">
 

/*

* Java CSV is a stream based library for reading and writing
* CSV and other delimited data.
*   
* Copyright (C) Bruce Dunwiddie bruce@csvreader.ru
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/

import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.nio.charset.Charset; import java.text.NumberFormat; import java.util.HashMap; /**

* A stream based parser for parsing delimited text data from a file or a
* stream.
*/

public class CsvReader {

 private Reader inputStream = null;
 private String fileName = null;
 // this holds all the values for switches that the user is allowed to set
 private UserSettings userSettings = new UserSettings();
 private Charset charset = null;
 private boolean useCustomRecordDelimiter = false;
 // this will be our working buffer to hold data chunks
 // read in from the data file
 private DataBuffer dataBuffer = new DataBuffer();
 private ColumnBuffer columnBuffer = new ColumnBuffer();
 private RawRecordBuffer rawBuffer = new RawRecordBuffer();
 private boolean[] isQualified = null;
 private String rawRecord = "";
 private HeadersHolder headersHolder = new HeadersHolder();
 // these are all more or less global loop variables
 // to keep from needing to pass them all into various
 // methods during parsing
 private boolean startedColumn = false;
 private boolean startedWithQualifier = false;
 private boolean hasMoreData = true;
 private char lastLetter = "\0";
 private boolean hasReadNextLine = false;
 private int columnsCount = 0;
 private long currentRecord = 0;
 private String[] values = new String[StaticSettings.INITIAL_COLUMN_COUNT];
 private boolean initialized = false;
 private boolean closed = false;
 /**
  * Double up the text qualifier to represent an occurance of the text
  * qualifier.
  */
 public static final int ESCAPE_MODE_DOUBLED = 1;
 /**
  * Use a backslash character before the text qualifier to represent an
  * occurance of the text qualifier.
  */
 public static final int ESCAPE_MODE_BACKSLASH = 2;
 /**
  * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
  * as the data source.
  * 
  * @param fileName
  *            The path to the file to use as the data source.
  * @param delimiter
  *            The character to use as the column delimiter.
  * @param charset
  *            The {@link java.nio.charset.Charset Charset} to use while
  *            parsing the data.
  */
 public CsvReader(String fileName, char delimiter, Charset charset)
     throws FileNotFoundException {
   if (fileName == null) {
     throw new IllegalArgumentException(
         "Parameter fileName can not be null.");
   }
   if (charset == null) {
     throw new IllegalArgumentException(
         "Parameter charset can not be null.");
   }
   if (!new File(fileName).exists()) {
     throw new FileNotFoundException("File " + fileName
         + " does not exist.");
   }
   this.fileName = fileName;
   this.userSettings.Delimiter = delimiter;
   this.charset = charset;
   isQualified = new boolean[values.length];
 }
 /**
  * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
  * as the data source. Uses ISO-8859-1 as the
  * {@link java.nio.charset.Charset Charset}.
  * 
  * @param fileName
  *            The path to the file to use as the data source.
  * @param delimiter
  *            The character to use as the column delimiter.
  */
 public CsvReader(String fileName, char delimiter)
     throws FileNotFoundException {
   this(fileName, delimiter, Charset.forName("ISO-8859-1"));
 }
 /**
  * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
  * as the data source. Uses a comma as the column delimiter and
  * ISO-8859-1 as the {@link java.nio.charset.Charset Charset}.
  * 
  * @param fileName
  *            The path to the file to use as the data source.
  */
 public CsvReader(String fileName) throws FileNotFoundException {
   this(fileName, Letters.ruMA);
 }
 /**
  * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
  * {@link java.io.Reader Reader} object as the data source.
  * 
  * @param inputStream
  *            The stream to use as the data source.
  * @param delimiter
  *            The character to use as the column delimiter.
  */
 public CsvReader(Reader inputStream, char delimiter) {
   if (inputStream == null) {
     throw new IllegalArgumentException(
         "Parameter inputStream can not be null.");
   }
   this.inputStream = inputStream;
   this.userSettings.Delimiter = delimiter;
   initialized = true;
   isQualified = new boolean[values.length];
 }
 /**
  * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
  * {@link java.io.Reader Reader} object as the data source. Uses a
  * comma as the column delimiter.
  * 
  * @param inputStream
  *            The stream to use as the data source.
  */
 public CsvReader(Reader inputStream) {
   this(inputStream, Letters.ruMA);
 }
 /**
  * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
  * {@link java.io.InputStream InputStream} object as the data source.
  * 
  * @param inputStream
  *            The stream to use as the data source.
  * @param delimiter
  *            The character to use as the column delimiter.
  * @param charset
  *            The {@link java.nio.charset.Charset Charset} to use while
  *            parsing the data.
  */
 public CsvReader(InputStream inputStream, char delimiter, Charset charset) {
   this(new InputStreamReader(inputStream, charset), delimiter);
 }
 /**
  * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
  * {@link java.io.InputStream InputStream} object as the data
  * source. Uses a comma as the column delimiter.
  * 
  * @param inputStream
  *            The stream to use as the data source.
  * @param charset
  *            The {@link java.nio.charset.Charset Charset} to use while
  *            parsing the data.
  */
 public CsvReader(InputStream inputStream, Charset charset) {
   this(new InputStreamReader(inputStream, charset));
 }
 public boolean getCaptureRawRecord() {
   return userSettings.CaptureRawRecord;
 }
 public void setCaptureRawRecord(boolean captureRawRecord) {
   userSettings.CaptureRawRecord = captureRawRecord;
 }
 public String getRawRecord() {
   return rawRecord;
 }
 /**
  * Gets whether leading and trailing whitespace characters are being trimmed
  * from non-textqualified column data. Default is true.
  * 
  * @return Whether leading and trailing whitespace characters are being
  *         trimmed from non-textqualified column data.
  */
 public boolean getTrimWhitespace() {
   return userSettings.TrimWhitespace;
 }
 /**
  * Sets whether leading and trailing whitespace characters should be trimmed
  * from non-textqualified column data or not. Default is true.
  * 
  * @param trimWhitespace
  *            Whether leading and trailing whitespace characters should be
  *            trimmed from non-textqualified column data or not.
  */
 public void setTrimWhitespace(boolean trimWhitespace) {
   userSettings.TrimWhitespace = trimWhitespace;
 }
 /**
  * Gets the character being used as the column delimiter. Default is comma,
  * ",".
  * 
  * @return The character being used as the column delimiter.
  */
 public char getDelimiter() {
   return userSettings.Delimiter;
 }
 /**
  * Sets the character to use as the column delimiter. Default is comma, ",".
  * 
  * @param delimiter
  *            The character to use as the column delimiter.
  */
 public void setDelimiter(char delimiter) {
   userSettings.Delimiter = delimiter;
 }
 public char getRecordDelimiter() {
   return userSettings.RecordDelimiter;
 }
 /**
  * Sets the character to use as the record delimiter.
  * 
  * @param recordDelimiter
  *            The character to use as the record delimiter. Default is
  *            combination of standard end of line characters for Windows,
  *            Unix, or Mac.
  */
 public void setRecordDelimiter(char recordDelimiter) {
   useCustomRecordDelimiter = true;
   userSettings.RecordDelimiter = recordDelimiter;
 }
 /**
  * Gets the character to use as a text qualifier in the data.
  * 
  * @return The character to use as a text qualifier in the data.
  */
 public char getTextQualifier() {
   return userSettings.TextQualifier;
 }
 /**
  * Sets the character to use as a text qualifier in the data.
  * 
  * @param textQualifier
  *            The character to use as a text qualifier in the data.
  */
 public void setTextQualifier(char textQualifier) {
   userSettings.TextQualifier = textQualifier;
 }
 /**
  * Whether text qualifiers will be used while parsing or not.
  * 
  * @return Whether text qualifiers will be used while parsing or not.
  */
 public boolean getUseTextQualifier() {
   return userSettings.UseTextQualifier;
 }
 /**
  * Sets whether text qualifiers will be used while parsing or not.
  * 
  * @param useTextQualifier
  *            Whether to use a text qualifier while parsing or not.
  */
 public void setUseTextQualifier(boolean useTextQualifier) {
   userSettings.UseTextQualifier = useTextQualifier;
 }
 /**
  * Gets the character being used as a comment signal.
  * 
  * @return The character being used as a comment signal.
  */
 public char getComment() {
   return userSettings.rument;
 }
 /**
  * Sets the character to use as a comment signal.
  * 
  * @param comment
  *            The character to use as a comment signal.
  */
 public void setComment(char comment) {
   userSettings.rument = comment;
 }
 /**
  * Gets whether comments are being looked for while parsing or not.
  * 
  * @return Whether comments are being looked for while parsing or not.
  */
 public boolean getUseComments() {
   return userSettings.UseComments;
 }
 /**
  * Sets whether comments are being looked for while parsing or not.
  * 
  * @param useComments
  *            Whether comments are being looked for while parsing or not.
  */
 public void setUseComments(boolean useComments) {
   userSettings.UseComments = useComments;
 }
 /**
  * Gets the current way to escape an occurance of the text qualifier inside
  * qualified data.
  * 
  * @return The current way to escape an occurance of the text qualifier
  *         inside qualified data.
  */
 public int getEscapeMode() {
   return userSettings.EscapeMode;
 }
 /**
  * Sets the current way to escape an occurance of the text qualifier inside
  * qualified data.
  * 
  * @param escapeMode
  *            The way to escape an occurance of the text qualifier inside
  *            qualified data.
  * @exception IllegalArgumentException
  *                When an illegal value is specified for escapeMode.
  */
 public void setEscapeMode(int escapeMode) throws IllegalArgumentException {
   if (escapeMode != ESCAPE_MODE_DOUBLED
       && escapeMode != ESCAPE_MODE_BACKSLASH) {
     throw new IllegalArgumentException(
         "Parameter escapeMode must be a valid value.");
   }
   userSettings.EscapeMode = escapeMode;
 }
 public boolean getSkipEmptyRecords() {
   return userSettings.SkipEmptyRecords;
 }
 public void setSkipEmptyRecords(boolean skipEmptyRecords) {
   userSettings.SkipEmptyRecords = skipEmptyRecords;
 }
 /**
  * Safety caution to prevent the parser from using large amounts of memory
  * in the case where parsing settings like file encodings don"t end up
  * matching the actual format of a file. This switch can be turned off if
  * the file format is known and tested. With the switch off, the max column
  * lengths and max column count per record supported by the parser will
  * greatly increase. Default is true.
  * 
  * @return The current setting of the safety switch.
  */
 public boolean getSafetySwitch() {
   return userSettings.SafetySwitch;
 }
 /**
  * Safety caution to prevent the parser from using large amounts of memory
  * in the case where parsing settings like file encodings don"t end up
  * matching the actual format of a file. This switch can be turned off if
  * the file format is known and tested. With the switch off, the max column
  * lengths and max column count per record supported by the parser will
  * greatly increase. Default is true.
  * 
  * @param safetySwitch
  */
 public void setSafetySwitch(boolean safetySwitch) {
   userSettings.SafetySwitch = safetySwitch;
 }
 /**
  * Gets the count of columns found in this record.
  * 
  * @return The count of columns found in this record.
  */
 public int getColumnCount() {
   return columnsCount;
 }
 /**
  * Gets the index of the current record.
  * 
  * @return The index of the current record.
  */
 public long getCurrentRecord() {
   return currentRecord - 1;
 }
 /**
  * Gets the count of headers read in by a previous call to
  * {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
  * 
  * @return The count of headers read in by a previous call to
  *         {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
  */
 public int getHeaderCount() {
   return headersHolder.Length;
 }
 /**
  * Returns the header values as a string array.
  * 
  * @return The header values as a String array.
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 public String[] getHeaders() throws IOException {
   checkClosed();
   if (headersHolder.Headers == null) {
     return null;
   } else {
     // use clone here to prevent the outside code from
     // setting values on the array directly, which would
     // throw off the index lookup based on header name
     String[] clone = new String[headersHolder.Length];
     System.arraycopy(headersHolder.Headers, 0, clone, 0,
         headersHolder.Length);
     return clone;
   }
 }
 public void setHeaders(String[] headers) {
   headersHolder.Headers = headers;
   headersHolder.IndexByName.clear();
   if (headers != null) {
     headersHolder.Length = headers.length;
   } else {
     headersHolder.Length = 0;
   }
   // use headersHolder.Length here in case headers is null
   for (int i = 0; i < headersHolder.Length; i++) {
     headersHolder.IndexByName.put(headers[i], new Integer(i));
   }
 }
 public String[] getValues() throws IOException {
   checkClosed();
   // need to return a clone, and can"t use clone because values.Length
   // might be greater than columnsCount
   String[] clone = new String[columnsCount];
   System.arraycopy(values, 0, clone, 0, columnsCount);
   return clone;
 }
 /**
  * Returns the current column value for a given column index.
  * 
  * @param columnIndex
  *            The index of the column.
  * @return The current column value.
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 public String get(int columnIndex) throws IOException {
   checkClosed();
   if (columnIndex > -1 && columnIndex < columnsCount) {
     return values[columnIndex];
   } else {
     return "";
   }
 }
 /**
  * Returns the current column value for a given column header name.
  * 
  * @param headerName
  *            The header name of the column.
  * @return The current column value.
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 public String get(String headerName) throws IOException {
   checkClosed();
   return get(getIndex(headerName));
 }
 /**
  * Creates a {@link com.csvreader.CsvReader CsvReader} object using a string
  * of data as the source. Uses ISO-8859-1 as the
  * {@link java.nio.charset.Charset Charset}.
  * 
  * @param data
  *            The String of data to use as the source.
  * @return A {@link com.csvreader.CsvReader CsvReader} object using the
  *         String of data as the source.
  */
 public static CsvReader parse(String data) {
   if (data == null) {
     throw new IllegalArgumentException(
         "Parameter data can not be null.");
   }
   return new CsvReader(new StringReader(data));
 }
 /**
  * Reads another record.
  * 
  * @return Whether another record was successfully read or not.
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 public boolean readRecord() throws IOException {
   checkClosed();
   columnsCount = 0;
   rawBuffer.Position = 0;
   dataBuffer.LineStart = dataBuffer.Position;
   hasReadNextLine = false;
   // check to see if we"ve already found the end of data
   if (hasMoreData) {
     // loop over the data stream until the end of data is found
     // or the end of the record is found
     do {
       if (dataBuffer.Position == dataBuffer.Count) {
         checkDataLength();
       } else {
         startedWithQualifier = false;
         // grab the current letter as a char
         char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
         if (userSettings.UseTextQualifier
             && currentLetter == userSettings.TextQualifier) {
           // this will be a text qualified column, so
           // we need to set startedWithQualifier to make it
           // enter the seperate branch to handle text
           // qualified columns
           lastLetter = currentLetter;
           // read qualified
           startedColumn = true;
           dataBuffer.ColumnStart = dataBuffer.Position + 1;
           startedWithQualifier = true;
           boolean lastLetterWasQualifier = false;
           char escapeChar = userSettings.TextQualifier;
           if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH) {
             escapeChar = Letters.BACKSLASH;
           }
           boolean eatingTrailingJunk = false;
           boolean lastLetterWasEscape = false;
           boolean readingComplexEscape = false;
           int escape = ComplexEscape.UNICODE;
           int escapeLength = 0;
           char escapeValue = (char) 0;
           dataBuffer.Position++;
           do {
             if (dataBuffer.Position == dataBuffer.Count) {
               checkDataLength();
             } else {
               // grab the current letter as a char
               currentLetter = dataBuffer.Buffer[dataBuffer.Position];
               if (eatingTrailingJunk) {
                 dataBuffer.ColumnStart = dataBuffer.Position + 1;
                 if (currentLetter == userSettings.Delimiter) {
                   endColumn();
                 } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                     || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                   endColumn();
                   endRecord();
                 }
               } else if (readingComplexEscape) {
                 escapeLength++;
                 switch (escape) {
                 case ComplexEscape.UNICODE:
                   escapeValue *= (char) 16;
                   escapeValue += hexToDec(currentLetter);
                   if (escapeLength == 4) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.OCTAL:
                   escapeValue *= (char) 8;
                   escapeValue += (char) (currentLetter - "0");
                   if (escapeLength == 3) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.DECIMAL:
                   escapeValue *= (char) 10;
                   escapeValue += (char) (currentLetter - "0");
                   if (escapeLength == 3) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.HEX:
                   escapeValue *= (char) 16;
                   escapeValue += hexToDec(currentLetter);
                   if (escapeLength == 2) {
                     readingComplexEscape = false;
                   }
                   break;
                 }
                 if (!readingComplexEscape) {
                   appendLetter(escapeValue);
                 } else {
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                 }
               } else if (currentLetter == userSettings.TextQualifier) {
                 if (lastLetterWasEscape) {
                   lastLetterWasEscape = false;
                   lastLetterWasQualifier = false;
                 } else {
                   updateCurrentValue();
                   if (userSettings.EscapeMode == ESCAPE_MODE_DOUBLED) {
                     lastLetterWasEscape = true;
                   }
                   lastLetterWasQualifier = true;
                 }
               } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                   && lastLetterWasEscape) {
                 switch (currentLetter) {
                 case "n":
                   appendLetter(Letters.LF);
                   break;
                 case "r":
                   appendLetter(Letters.CR);
                   break;
                 case "t":
                   appendLetter(Letters.TAB);
                   break;
                 case "b":
                   appendLetter(Letters.BACKSPACE);
                   break;
                 case "f":
                   appendLetter(Letters.FORM_FEED);
                   break;
                 case "e":
                   appendLetter(Letters.ESCAPE);
                   break;
                 case "v":
                   appendLetter(Letters.VERTICAL_TAB);
                   break;
                 case "a":
                   appendLetter(Letters.ALERT);
                   break;
                 case "0":
                 case "1":
                 case "2":
                 case "3":
                 case "4":
                 case "5":
                 case "6":
                 case "7":
                   escape = ComplexEscape.OCTAL;
                   readingComplexEscape = true;
                   escapeLength = 1;
                   escapeValue = (char) (currentLetter - "0");
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                   break;
                 case "u":
                 case "x":
                 case "o":
                 case "d":
                 case "U":
                 case "X":
                 case "O":
                 case "D":
                   switch (currentLetter) {
                   case "u":
                   case "U":
                     escape = ComplexEscape.UNICODE;
                     break;
                   case "x":
                   case "X":
                     escape = ComplexEscape.HEX;
                     break;
                   case "o":
                   case "O":
                     escape = ComplexEscape.OCTAL;
                     break;
                   case "d":
                   case "D":
                     escape = ComplexEscape.DECIMAL;
                     break;
                   }
                   readingComplexEscape = true;
                   escapeLength = 0;
                   escapeValue = (char) 0;
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                   break;
                 default:
                   break;
                 }
                 lastLetterWasEscape = false;
                 // can only happen for ESCAPE_MODE_BACKSLASH
               } else if (currentLetter == escapeChar) {
                 updateCurrentValue();
                 lastLetterWasEscape = true;
               } else {
                 if (lastLetterWasQualifier) {
                   if (currentLetter == userSettings.Delimiter) {
                     endColumn();
                   } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                       || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                     endColumn();
                     endRecord();
                   } else {
                     dataBuffer.ColumnStart = dataBuffer.Position + 1;
                     eatingTrailingJunk = true;
                   }
                   // make sure to clear the flag for next
                   // run of the loop
                   lastLetterWasQualifier = false;
                 }
               }
               // keep track of the last letter because we need
               // it for several key decisions
               lastLetter = currentLetter;
               if (startedColumn) {
                 dataBuffer.Position++;
                 if (userSettings.SafetySwitch
                     && dataBuffer.Position
                         - dataBuffer.ColumnStart
                         + columnBuffer.Position > 100000) {
                   close();
                   throw new IOException(
                       "Maximum column length of 100,000 exceeded in column "
                           + NumberFormat
                               .getIntegerInstance()
                               .format(
                                   columnsCount)
                           + " in record "
                           + NumberFormat
                               .getIntegerInstance()
                               .format(
                                   currentRecord)
                           + ". Set the SafetySwitch property to false"
                           + " if you"re expecting column lengths greater than 100,000 characters to"
                           + " avoid this error.");
                 }
               }
             } // end else
           } while (hasMoreData && startedColumn);
         } else if (currentLetter == userSettings.Delimiter) {
           // we encountered a column with no data, so
           // just send the end column
           lastLetter = currentLetter;
           endColumn();
         } else if (useCustomRecordDelimiter
             && currentLetter == userSettings.RecordDelimiter) {
           // this will skip blank lines
           if (startedColumn || columnsCount > 0
               || !userSettings.SkipEmptyRecords) {
             endColumn();
             endRecord();
           } else {
             dataBuffer.LineStart = dataBuffer.Position + 1;
           }
           lastLetter = currentLetter;
         } else if (!useCustomRecordDelimiter
             && (currentLetter == Letters.CR || currentLetter == Letters.LF)) {
           // this will skip blank lines
           if (startedColumn
               || columnsCount > 0
               || (!userSettings.SkipEmptyRecords && (currentLetter == Letters.CR || lastLetter != Letters.CR))) {
             endColumn();
             endRecord();
           } else {
             dataBuffer.LineStart = dataBuffer.Position + 1;
           }
           lastLetter = currentLetter;
         } else if (userSettings.UseComments && columnsCount == 0
             && currentLetter == userSettings.rument) {
           // encountered a comment character at the beginning of
           // the line so just ignore the rest of the line
           lastLetter = currentLetter;
           skipLine();
         } else if (userSettings.TrimWhitespace
             && (currentLetter == Letters.SPACE || currentLetter == Letters.TAB)) {
           // do nothing, this will trim leading whitespace
           // for both text qualified columns and non
           startedColumn = true;
           dataBuffer.ColumnStart = dataBuffer.Position + 1;
         } else {
           // since the letter wasn"t a special letter, this
           // will be the first letter of our current column
           startedColumn = true;
           dataBuffer.ColumnStart = dataBuffer.Position;
           boolean lastLetterWasBackslash = false;
           boolean readingComplexEscape = false;
           int escape = ComplexEscape.UNICODE;
           int escapeLength = 0;
           char escapeValue = (char) 0;
           boolean firstLoop = true;
           do {
             if (!firstLoop
                 && dataBuffer.Position == dataBuffer.Count) {
               checkDataLength();
             } else {
               if (!firstLoop) {
                 // grab the current letter as a char
                 currentLetter = dataBuffer.Buffer[dataBuffer.Position];
               }
               if (!userSettings.UseTextQualifier
                   && userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                   && currentLetter == Letters.BACKSLASH) {
                 if (lastLetterWasBackslash) {
                   lastLetterWasBackslash = false;
                 } else {
                   updateCurrentValue();
                   lastLetterWasBackslash = true;
                 }
               } else if (readingComplexEscape) {
                 escapeLength++;
                 switch (escape) {
                 case ComplexEscape.UNICODE:
                   escapeValue *= (char) 16;
                   escapeValue += hexToDec(currentLetter);
                   if (escapeLength == 4) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.OCTAL:
                   escapeValue *= (char) 8;
                   escapeValue += (char) (currentLetter - "0");
                   if (escapeLength == 3) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.DECIMAL:
                   escapeValue *= (char) 10;
                   escapeValue += (char) (currentLetter - "0");
                   if (escapeLength == 3) {
                     readingComplexEscape = false;
                   }
                   break;
                 case ComplexEscape.HEX:
                   escapeValue *= (char) 16;
                   escapeValue += hexToDec(currentLetter);
                   if (escapeLength == 2) {
                     readingComplexEscape = false;
                   }
                   break;
                 }
                 if (!readingComplexEscape) {
                   appendLetter(escapeValue);
                 } else {
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                 }
               } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                   && lastLetterWasBackslash) {
                 switch (currentLetter) {
                 case "n":
                   appendLetter(Letters.LF);
                   break;
                 case "r":
                   appendLetter(Letters.CR);
                   break;
                 case "t":
                   appendLetter(Letters.TAB);
                   break;
                 case "b":
                   appendLetter(Letters.BACKSPACE);
                   break;
                 case "f":
                   appendLetter(Letters.FORM_FEED);
                   break;
                 case "e":
                   appendLetter(Letters.ESCAPE);
                   break;
                 case "v":
                   appendLetter(Letters.VERTICAL_TAB);
                   break;
                 case "a":
                   appendLetter(Letters.ALERT);
                   break;
                 case "0":
                 case "1":
                 case "2":
                 case "3":
                 case "4":
                 case "5":
                 case "6":
                 case "7":
                   escape = ComplexEscape.OCTAL;
                   readingComplexEscape = true;
                   escapeLength = 1;
                   escapeValue = (char) (currentLetter - "0");
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                   break;
                 case "u":
                 case "x":
                 case "o":
                 case "d":
                 case "U":
                 case "X":
                 case "O":
                 case "D":
                   switch (currentLetter) {
                   case "u":
                   case "U":
                     escape = ComplexEscape.UNICODE;
                     break;
                   case "x":
                   case "X":
                     escape = ComplexEscape.HEX;
                     break;
                   case "o":
                   case "O":
                     escape = ComplexEscape.OCTAL;
                     break;
                   case "d":
                   case "D":
                     escape = ComplexEscape.DECIMAL;
                     break;
                   }
                   readingComplexEscape = true;
                   escapeLength = 0;
                   escapeValue = (char) 0;
                   dataBuffer.ColumnStart = dataBuffer.Position + 1;
                   break;
                 default:
                   break;
                 }
                 lastLetterWasBackslash = false;
               } else {
                 if (currentLetter == userSettings.Delimiter) {
                   endColumn();
                 } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                     || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                   endColumn();
                   endRecord();
                 }
               }
               // keep track of the last letter because we need
               // it for several key decisions
               lastLetter = currentLetter;
               firstLoop = false;
               if (startedColumn) {
                 dataBuffer.Position++;
                 if (userSettings.SafetySwitch
                     && dataBuffer.Position
                         - dataBuffer.ColumnStart
                         + columnBuffer.Position > 100000) {
                   close();
                   throw new IOException(
                       "Maximum column length of 100,000 exceeded in column "
                           + NumberFormat
                               .getIntegerInstance()
                               .format(
                                   columnsCount)
                           + " in record "
                           + NumberFormat
                               .getIntegerInstance()
                               .format(
                                   currentRecord)
                           + ". Set the SafetySwitch property to false"
                           + " if you"re expecting column lengths greater than 100,000 characters to"
                           + " avoid this error.");
                 }
               }
             } // end else
           } while (hasMoreData && startedColumn);
         }
         if (hasMoreData) {
           dataBuffer.Position++;
         }
       } // end else
     } while (hasMoreData && !hasReadNextLine);
     // check to see if we hit the end of the file
     // without processing the current record
     if (startedColumn || lastLetter == userSettings.Delimiter) {
       endColumn();
       endRecord();
     }
   }
   if (userSettings.CaptureRawRecord) {
     if (hasMoreData) {
       if (rawBuffer.Position == 0) {
         rawRecord = new String(dataBuffer.Buffer,
             dataBuffer.LineStart, dataBuffer.Position
                 - dataBuffer.LineStart - 1);
       } else {
         rawRecord = new String(rawBuffer.Buffer, 0,
             rawBuffer.Position)
             + new String(dataBuffer.Buffer,
                 dataBuffer.LineStart, dataBuffer.Position
                     - dataBuffer.LineStart - 1);
       }
     } else {
       // for hasMoreData to ever be false, all data would have had to
       // have been
       // copied to the raw buffer
       rawRecord = new String(rawBuffer.Buffer, 0, rawBuffer.Position);
     }
   } else {
     rawRecord = "";
   }
   return hasReadNextLine;
 }
 /**
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 private void checkDataLength() throws IOException {
   if (!initialized) {
     if (fileName != null) {
       inputStream = new BufferedReader(new InputStreamReader(
           new FileInputStream(fileName), charset),
           StaticSettings.MAX_FILE_BUFFER_SIZE);
     }
     charset = null;
     initialized = true;
   }
   updateCurrentValue();
   if (userSettings.CaptureRawRecord && dataBuffer.Count > 0) {
     if (rawBuffer.Buffer.length - rawBuffer.Position < dataBuffer.Count
         - dataBuffer.LineStart) {
       int newLength = rawBuffer.Buffer.length
           + Math.max(dataBuffer.Count - dataBuffer.LineStart,
               rawBuffer.Buffer.length);
       char[] holder = new char[newLength];
       System.arraycopy(rawBuffer.Buffer, 0, holder, 0,
           rawBuffer.Position);
       rawBuffer.Buffer = holder;
     }
     System.arraycopy(dataBuffer.Buffer, dataBuffer.LineStart,
         rawBuffer.Buffer, rawBuffer.Position, dataBuffer.Count
             - dataBuffer.LineStart);
     rawBuffer.Position += dataBuffer.Count - dataBuffer.LineStart;
   }
   try {
     dataBuffer.Count = inputStream.read(dataBuffer.Buffer, 0,
         dataBuffer.Buffer.length);
   } catch (IOException ex) {
     close();
     throw ex;
   }
   // if no more data could be found, set flag stating that
   // the end of the data was found
   if (dataBuffer.Count == -1) {
     hasMoreData = false;
   }
   dataBuffer.Position = 0;
   dataBuffer.LineStart = 0;
   dataBuffer.ColumnStart = 0;
 }
 /**
  * Read the first record of data as column headers.
  * 
  * @return Whether the header record was successfully read or not.
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 public boolean readHeaders() throws IOException {
   boolean result = readRecord();
   // copy the header data from the column array
   // to the header string array
   headersHolder.Length = columnsCount;
   headersHolder.Headers = new String[columnsCount];
   for (int i = 0; i < headersHolder.Length; i++) {
     String columnValue = get(i);
     headersHolder.Headers[i] = columnValue;
     // if there are duplicate header names, we will save the last one
     headersHolder.IndexByName.put(columnValue, new Integer(i));
   }
   if (result) {
     currentRecord--;
   }
   columnsCount = 0;
   return result;
 }
 /**
  * Returns the column header value for a given column index.
  * 
  * @param columnIndex
  *            The index of the header column being requested.
  * @return The value of the column header at the given column index.
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 public String getHeader(int columnIndex) throws IOException {
   checkClosed();
   // check to see if we have read the header record yet
   // check to see if the column index is within the bounds
   // of our header array
   if (columnIndex > -1 && columnIndex < headersHolder.Length) {
     // return the processed header data for this column
     return headersHolder.Headers[columnIndex];
   } else {
     return "";
   }
 }
 public boolean isQualified(int columnIndex) throws IOException {
   checkClosed();
   if (columnIndex < columnsCount && columnIndex > -1) {
     return isQualified[columnIndex];
   } else {
     return false;
   }
 }
 /**
  * @exception IOException
  *                Thrown if a very rare extreme exception occurs during
  *                parsing, normally resulting from improper data format.
  */
 private void endColumn() throws IOException {
   String currentValue = "";
   // must be called before setting startedColumn = false
   if (startedColumn) {
     if (columnBuffer.Position == 0) {
       if (dataBuffer.ColumnStart < dataBuffer.Position) {
         int lastLetter = dataBuffer.Position - 1;
         if (userSettings.TrimWhitespace && !startedWithQualifier) {
           while (lastLetter >= dataBuffer.ColumnStart
               && (dataBuffer.Buffer[lastLetter] == Letters.SPACE || dataBuffer.Buffer[lastLetter] == Letters.TAB)) {
             lastLetter--;
           }
         }
         currentValue = new String(dataBuffer.Buffer,
             dataBuffer.ColumnStart, lastLetter
                 - dataBuffer.ColumnStart + 1);
       }
     } else {
       updateCurrentValue();
       int lastLetter = columnBuffer.Position - 1;
       if (userSettings.TrimWhitespace && !startedWithQualifier) {
         while (lastLetter >= 0
             && (columnBuffer.Buffer[lastLetter] == Letters.SPACE || columnBuffer.Buffer[lastLetter] == Letters.SPACE)) {
           lastLetter--;
         }
       }
       currentValue = new String(columnBuffer.Buffer, 0,
           lastLetter + 1);
     }
   }
   columnBuffer.Position = 0;
   startedColumn = false;
   if (columnsCount >= 100000 && userSettings.SafetySwitch) {
     close();
     throw new IOException(
         "Maximum column count of 100,000 exceeded in record "
             + NumberFormat.getIntegerInstance().format(
                 currentRecord)
             + ". Set the SafetySwitch property to false"
             + " if you"re expecting more than 100,000 columns per record to"
             + " avoid this error.");
   }
   // check to see if our current holder array for
   // column chunks is still big enough to handle another
   // column chunk
   if (columnsCount == values.length) {
     // holder array needs to grow to be able to hold another column
     int newLength = values.length * 2;
     String[] holder = new String[newLength];
     System.arraycopy(values, 0, holder, 0, values.length);
     values = holder;
     boolean[] qualifiedHolder = new boolean[newLength];
     System.arraycopy(isQualified, 0, qualifiedHolder, 0,
         isQualified.length);
     isQualified = qualifiedHolder;
   }
   values[columnsCount] = currentValue;
   isQualified[columnsCount] = startedWithQualifier;
   currentValue = "";
   columnsCount++;
 }
 private void appendLetter(char letter) {
   if (columnBuffer.Position == columnBuffer.Buffer.length) {
     int newLength = columnBuffer.Buffer.length * 2;
     char[] holder = new char[newLength];
     System.arraycopy(columnBuffer.Buffer, 0, holder, 0,
         columnBuffer.Position);
     columnBuffer.Buffer = holder;
   }
   columnBuffer.Buffer[columnBuffer.Position++] = letter;
   dataBuffer.ColumnStart = dataBuffer.Position + 1;
 }
 private void updateCurrentValue() {
   if (startedColumn && dataBuffer.ColumnStart < dataBuffer.Position) {
     if (columnBuffer.Buffer.length - columnBuffer.Position < dataBuffer.Position
         - dataBuffer.ColumnStart) {
       int newLength = columnBuffer.Buffer.length
           + Math.max(
               dataBuffer.Position - dataBuffer.ColumnStart,
               columnBuffer.Buffer.length);
       char[] holder = new char[newLength];
       System.arraycopy(columnBuffer.Buffer, 0, holder, 0,
           columnBuffer.Position);
       columnBuffer.Buffer = holder;
     }
     System.arraycopy(dataBuffer.Buffer, dataBuffer.ColumnStart,
         columnBuffer.Buffer, columnBuffer.Position,
         dataBuffer.Position - dataBuffer.ColumnStart);
     columnBuffer.Position += dataBuffer.Position
         - dataBuffer.ColumnStart;
   }
   dataBuffer.ColumnStart = dataBuffer.Position + 1;
 }
 /**
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 private void endRecord() throws IOException {
   // this flag is used as a loop exit condition
   // during parsing
   hasReadNextLine = true;
   currentRecord++;
 }
 /**
  * Gets the corresponding column index for a given column header name.
  * 
  * @param headerName
  *            The header name of the column.
  * @return The column index for the given column header name. Returns
  *         -1 if not found.
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 public int getIndex(String headerName) throws IOException {
   checkClosed();
   Object indexValue = headersHolder.IndexByName.get(headerName);
   if (indexValue != null) {
     return ((Integer) indexValue).intValue();
   } else {
     return -1;
   }
 }
 /**
  * Skips the next record of data by parsing each column. Does not
  * increment
  * {@link com.csvreader.CsvReader#getCurrentRecord getCurrentRecord()}.
  * 
  * @return Whether another record was successfully skipped or not.
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 public boolean skipRecord() throws IOException {
   checkClosed();
   boolean recordRead = false;
   if (hasMoreData) {
     recordRead = readRecord();
     if (recordRead) {
       currentRecord--;
     }
   }
   return recordRead;
 }
 /**
  * Skips the next line of data using the standard end of line characters and
  * does not do any column delimited parsing.
  * 
  * @return Whether a line was successfully skipped or not.
  * @exception IOException
  *                Thrown if an error occurs while reading data from the
  *                source stream.
  */
 public boolean skipLine() throws IOException {
   checkClosed();
   // clear public column values for current line
   columnsCount = 0;
   boolean skippedLine = false;
   if (hasMoreData) {
     boolean foundEol = false;
     do {
       if (dataBuffer.Position == dataBuffer.Count) {
         checkDataLength();
       } else {
         skippedLine = true;
         // grab the current letter as a char
         char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
         if (currentLetter == Letters.CR
             || currentLetter == Letters.LF) {
           foundEol = true;
         }
         // keep track of the last letter because we need
         // it for several key decisions
         lastLetter = currentLetter;
         if (!foundEol) {
           dataBuffer.Position++;
         }
       } // end else
     } while (hasMoreData && !foundEol);
     columnBuffer.Position = 0;
     dataBuffer.LineStart = dataBuffer.Position + 1;
   }
   rawBuffer.Position = 0;
   rawRecord = "";
   return skippedLine;
 }
 /**
  * Closes and releases all related resources.
  */
 public void close() {
   if (!closed) {
     close(true);
     closed = true;
   }
 }
 /**
  * 
  */
 private void close(boolean closing) {
   if (!closed) {
     if (closing) {
       charset = null;
       headersHolder.Headers = null;
       headersHolder.IndexByName = null;
       dataBuffer.Buffer = null;
       columnBuffer.Buffer = null;
       rawBuffer.Buffer = null;
     }
     try {
       if (initialized) {
         inputStream.close();
       }
     } catch (Exception e) {
       // just eat the exception
     }
     inputStream = null;
     closed = true;
   }
 }
 /**
  * @exception IOException
  *                Thrown if this object has already been closed.
  */
 private void checkClosed() throws IOException {
   if (closed) {
     throw new IOException(
         "This instance of the CsvReader class has already been closed.");
   }
 }
 /**
  * 
  */
 protected void finalize() {
   close(false);
 }
 private class ComplexEscape {
   private static final int UNICODE = 1;
   private static final int OCTAL = 2;
   private static final int DECIMAL = 3;
   private static final int HEX = 4;
 }
 private static char hexToDec(char hex) {
   char result;
   if (hex >= "a") {
     result = (char) (hex - "a" + 10);
   } else if (hex >= "A") {
     result = (char) (hex - "A" + 10);
   } else {
     result = (char) (hex - "0");
   }
   return result;
 }
 private class DataBuffer {
   public char[] Buffer;
   public int Position;
   // / <summary>
   // / How much usable data has been read into the stream,
   // / which will not always be as long as Buffer.Length.
   // / </summary>
   public int Count;
   // / <summary>
   // / The position of the cursor in the buffer when the
   // / current column was started or the last time data
   // / was moved out to the column buffer.
   // / </summary>
   public int ColumnStart;
   public int LineStart;
   public DataBuffer() {
     Buffer = new char[StaticSettings.MAX_BUFFER_SIZE];
     Position = 0;
     Count = 0;
     ColumnStart = 0;
     LineStart = 0;
   }
 }
 private class ColumnBuffer {
   public char[] Buffer;
   public int Position;
   public ColumnBuffer() {
     Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE];
     Position = 0;
   }
 }
 private class RawRecordBuffer {
   public char[] Buffer;
   public int Position;
   public RawRecordBuffer() {
     Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE
         * StaticSettings.INITIAL_COLUMN_COUNT];
     Position = 0;
   }
 }
 private class Letters {
   public static final char LF = "\n";
   public static final char CR = "\r";
   public static final char QUOTE = """;
   public static final char COMMA = ",";
   public static final char SPACE = " ";
   public static final char TAB = "\t";
   public static final char POUND = "#";
   public static final char BACKSLASH = "\\";
   public static final char NULL = "\0";
   public static final char BACKSPACE = "\b";
   public static final char FORM_FEED = "\f";
   public static final char ESCAPE = "\u001B"; // ASCII/ANSI escape
   public static final char VERTICAL_TAB = "\u000B";
   public static final char ALERT = "\u0007";
 }
 private class UserSettings {
   // having these as publicly accessible members will prevent
   // the overhead of the method call that exists on properties
   public boolean CaseSensitive;
   public char TextQualifier;
   public boolean TrimWhitespace;
   public boolean UseTextQualifier;
   public char Delimiter;
   public char RecordDelimiter;
   public char Comment;
   public boolean UseComments;
   public int EscapeMode;
   public boolean SafetySwitch;
   public boolean SkipEmptyRecords;
   public boolean CaptureRawRecord;
   public UserSettings() {
     CaseSensitive = true;
     TextQualifier = Letters.QUOTE;
     TrimWhitespace = true;
     UseTextQualifier = true;
     Delimiter = Letters.ruMA;
     RecordDelimiter = Letters.NULL;
     Comment = Letters.POUND;
     UseComments = false;
     EscapeMode = CsvReader.ESCAPE_MODE_DOUBLED;
     SafetySwitch = true;
     SkipEmptyRecords = true;
     CaptureRawRecord = true;
   }
 }
 private class HeadersHolder {
   public String[] Headers;
   public int Length;
   public HashMap IndexByName;
   public HeadersHolder() {
     Headers = null;
     Length = 0;
     IndexByName = new HashMap();
   }
 }
 private class StaticSettings {
   // these are static instead of final so they can be changed in unit test
   // isn"t visible outside this class and is only accessed once during
   // CsvReader construction
   public static final int MAX_BUFFER_SIZE = 1024;
   public static final int MAX_FILE_BUFFER_SIZE = 4 * 1024;
   public static final int INITIAL_COLUMN_COUNT = 10;
   public static final int INITIAL_COLUMN_BUFFER_SIZE = 50;
 }

}


 </source>
   
  
 
  



A utility class that parses a Comma Separated Values (CSV) file

   <source lang="java">
 

/* Java and XSLT By Eric M.Burke 1st Edition September 2001 ISBN: 0-596-00143-6

  • /

import java.io.IOException; import java.util.*; import org.xml.sax.*;

import java.io.*; import java.net.URL; import org.xml.sax.*; import org.xml.sax.helpers.*;

/**

* A utility class that parses a Comma Separated Values (CSV) file
* and outputs its contents using SAX2 events. The format of CSV that
* this class reads is identical to the export format for Microsoft
* Excel. For simple values, the CSV file may look like this:
*
 * a,b,c
 * d,e,f
 * 
* Quotes are used as delimiters when the values contain commas:
*
 * a,"b,c",d
 * e,"f,g","h,i"
 * 
* And double quotes are used when the values contain quotes. This parser
* is smart enough to trim spaces around commas, as well.
*
* @author Eric M. Burke
*/

public class CSVXMLReader extends AbstractXMLReader {

   // an empty attribute for use with SAX
   private static final Attributes EMPTY_ATTR = new AttributesImpl();
   /**
    * Parse a CSV file. SAX events are delivered to the ContentHandler
    * that was registered via setContentHandler.
    *
    * @param input the comma separated values file to parse.
    */
   public void parse(InputSource input) throws IOException,
           SAXException {
       // if no handler is registered to receive events, don"t bother
       // to parse the CSV file
       ContentHandler ch = getContentHandler();
       if (ch == null) {
           return;
       }
       // convert the InputSource into a BufferedReader
       BufferedReader br = null;
       if (input.getCharacterStream() != null) {
           br = new BufferedReader(input.getCharacterStream());
       } else if (input.getByteStream() != null) {
           br = new BufferedReader(new InputStreamReader(
                   input.getByteStream()));
       } else if (input.getSystemId() != null) {
           java.net.URL url = new URL(input.getSystemId());
           br = new BufferedReader(new InputStreamReader(url.openStream()));
       } else {
           throw new SAXException("Invalid InputSource object");
       }
       ch.startDocument();
       // emit <csvFile>
       ch.startElement("","","csvFile",EMPTY_ATTR);
       // read each line of the file until EOF is reached
       String curLine = null;
       while ((curLine = br.readLine()) != null) {
           curLine = curLine.trim();
           if (curLine.length() > 0) {
               // create the <line> element
               ch.startElement("","","line",EMPTY_ATTR);
               // output data from this line
               parseLine(curLine, ch);
               // close the </line> element
               ch.endElement("","","line");
           }
       }
       // emit </csvFile>
       ch.endElement("","","csvFile");
       ch.endDocument();
   }
   // Break an individual line into tokens. This is a recursive function
   // that extracts the first token, then recursively parses the
   // remainder of the line.
   private void parseLine(String curLine, ContentHandler ch)
       throws IOException, SAXException {
       String firstToken = null;
       String remainderOfLine = null;
       int commaIndex = locateFirstDelimiter(curLine);
       if (commaIndex > -1) {
           firstToken = curLine.substring(0, commaIndex).trim();
           remainderOfLine = curLine.substring(commaIndex+1).trim();
       } else {
           // no commas, so the entire line is the token
           firstToken = curLine;
       }
       // remove redundant quotes
       firstToken = cleanupQuotes(firstToken);
       // emit the <value> element
       ch.startElement("","","value",EMPTY_ATTR);
       ch.characters(firstToken.toCharArray(), 0, firstToken.length());
       ch.endElement("","","value");
       // recursively process the remainder of the line
       if (remainderOfLine != null) {
           parseLine(remainderOfLine, ch);
       }
   }
   // locate the position of the comma, taking into account that
   // a quoted token may contain ignorable commas.
   private int locateFirstDelimiter(String curLine) {
       if (curLine.startsWith("\"")) {
           boolean inQuote = true;
           int numChars = curLine.length();
           for (int i=1; i<numChars; i++) {
               char curChar = curLine.charAt(i);
               if (curChar == """) {
                   inQuote = !inQuote;
               } else if (curChar == "," && !inQuote) {
                   return i;
               }
           }
           return -1;
       } else {
           return curLine.indexOf(",");
       }
   }
   // remove quotes around a token, as well as pairs of quotes
   // within a token.
   private String cleanupQuotes(String token) {
       StringBuffer buf = new StringBuffer();
       int length = token.length();
       int curIndex = 0;
       if (token.startsWith("\"") && token.endsWith("\"")) {
           curIndex = 1;
           length--;
       }
       boolean oneQuoteFound = false;
       boolean twoQuotesFound = false;
       while (curIndex < length) {
           char curChar = token.charAt(curIndex);
           if (curChar == """) {
               twoQuotesFound = (oneQuoteFound) ? true : false;
               oneQuoteFound = true;
           } else {
               oneQuoteFound = false;
               twoQuotesFound = false;
           }
           if (twoQuotesFound) {
               twoQuotesFound = false;
               oneQuoteFound = false;
               curIndex++;
               continue;
           }
           buf.append(curChar);
           curIndex++;
       }
       return buf.toString();
   }

} /**

* An abstract class that implements the SAX2 XMLReader interface. The
* intent of this class is to make it easy for subclasses to act as
* SAX2 XMLReader implementations. This makes it possible, for example, for
* them to emit SAX2 events that can be fed into an XSLT processor for
* transformation.
*
* @author Eric M. Burke
*/

abstract class AbstractXMLReader implements org.xml.sax.XMLReader {

   private Map featureMap = new HashMap();
   private Map propertyMap = new HashMap();
   private EntityResolver entityResolver;
   private DTDHandler dtdHandler;
   private ContentHandler contentHandler;
   private ErrorHandler errorHandler;
   /**
    * The only abstract method in this class. Derived classes can parse
    * any source of data and emit SAX2 events to the ContentHandler.
    */
   public abstract void parse(InputSource input) throws IOException,
           SAXException;
   public boolean getFeature(String name)
           throws SAXNotRecognizedException, SAXNotSupportedException {
       Boolean featureValue = (Boolean) this.featureMap.get(name);
       return (featureValue == null) ? false
               : featureValue.booleanValue();
   }
   public void setFeature(String name, boolean value)
           throws SAXNotRecognizedException, SAXNotSupportedException {
       this.featureMap.put(name, new Boolean(value));
   }
   public Object getProperty(String name)
           throws SAXNotRecognizedException, SAXNotSupportedException {
       return this.propertyMap.get(name);
   }
   public void setProperty(String name, Object value)
           throws SAXNotRecognizedException, SAXNotSupportedException {
       this.propertyMap.put(name, value);
   }
   public void setEntityResolver(EntityResolver entityResolver) {
       this.entityResolver = entityResolver;
   }
   public EntityResolver getEntityResolver() {
       return this.entityResolver;
   }
   public void setDTDHandler(DTDHandler dtdHandler) {
       this.dtdHandler = dtdHandler;
   }
   public DTDHandler getDTDHandler() {
       return this.dtdHandler;
   }
   public void setContentHandler(ContentHandler contentHandler) {
       this.contentHandler = contentHandler;
   }
   public ContentHandler getContentHandler() {
       return this.contentHandler;
   }
   public void setErrorHandler(ErrorHandler errorHandler) {
       this.errorHandler = errorHandler;
   }
   public ErrorHandler getErrorHandler() {
       return this.errorHandler;
   }
   public void parse(String systemId) throws IOException, SAXException {
       parse(new InputSource(systemId));
   }

}



 </source>
   
  
 
  



Builds a bracketed CSV list from the array

   <source lang="java">
 

import java.lang.reflect.Array; /* Copyright (c) 2001-2009, The HSQL Development Group

* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/** Provides a collection of convenience methods for processing and

* creating objects with String value components.
*
* @author Campbell Boucher-Burnett (boucherb@users dot sourceforge.net)
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @author Nitin Chauhan
* @version 1.9.0
* @since 1.7.0
*/

public class Main {

 /**
  * Builds a bracketed CSV list from the array
  * @param array an array of Objects
  * @return string
  */
 public static String arrayToString(Object array) {
     int          len  = Array.getLength(array);
     int          last = len - 1;
     StringBuffer sb   = new StringBuffer(2 * (len + 1));
     sb.append("{");
     for (int i = 0; i < len; i++) {
         sb.append(Array.get(array, i));
         if (i != last) {
             sb.append(",");
         }
     }
     sb.append("}");
     return sb.toString();
 }

}


 </source>
   
  
 
  



Builds a CSV list from the specified String[], separator string and quote string

   <source lang="java">
 
  
  

import java.lang.reflect.Array; /* Copyright (c) 2001-2009, The HSQL Development Group

* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/** Provides a collection of convenience methods for processing and

* creating objects with String value components.
*
* @author Campbell Boucher-Burnett (boucherb@users dot sourceforge.net)
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @author Nitin Chauhan
* @version 1.9.0
* @since 1.7.0
*/

public class Main {

 /**
  * Builds a CSV list from the specified String[], separator string and
* quote string.

* *

    *
  • All arguments are assumed to be non-null. *
  • Separates each list element with the value of the * separator argument. *
  • Prepends and appends each element with the value of the * quote argument. *
  • No attempt is made to escape the quote character sequence if it is * found internal to a list element. *
      * @return a CSV list * @param separator the String to use as the list element separator * @param quote the String with which to quote the list elements * @param s array of String objects */ public static String getList(String[] s, String separator, String quote) { int len = s.length; StringBuffer sb = new StringBuffer(len * 16); for (int i = 0; i < len; i++) { sb.append(quote); sb.append(s[i]); sb.append(quote); if (i + 1 < len) { sb.append(separator); } } return sb.toString(); } } </source>

      Builds a CSV list from the specified two dimensional String[][], separator string and quote string.

         <source lang="java">
       
        
      

      import java.lang.reflect.Array; /* Copyright (c) 2001-2009, The HSQL Development Group

      * All rights reserved.
      *
      * Redistribution and use in source and binary forms, with or without
      * modification, are permitted provided that the following conditions are met:
      *
      * Redistributions of source code must retain the above copyright notice, this
      * list of conditions and the following disclaimer.
      *
      * Redistributions in binary form must reproduce the above copyright notice,
      * this list of conditions and the following disclaimer in the documentation
      * and/or other materials provided with the distribution.
      *
      * Neither the name of the HSQL Development Group nor the names of its
      * contributors may be used to endorse or promote products derived from this
      * software without specific prior written permission.
      *
      * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
      * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
      * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      */
      

      /** Provides a collection of convenience methods for processing and

      * creating objects with String value components.
      *
      * @author Campbell Boucher-Burnett (boucherb@users dot sourceforge.net)
      * @author Fred Toussi (fredt@users dot sourceforge.net)
      * @author Nitin Chauhan
      * @version 1.9.0
      * @since 1.7.0
      */
      

      public class Main {

       /**
        * Builds a CSV list from the specified String[][], separator string and
        * quote string. <p>
        *
      
      *
        *
      • All arguments are assumed to be non-null. *
      • Uses only the first element in each subarray. *
      • Separates each list element with the value of the * separator argument. *
      • Prepends and appends each element with the value of the * quote argument. *
      • No attempt is made to escape the quote character sequence if it is * found internal to a list element. *
          * @return a CSV list * @param separator the String to use as the list element separator * @param quote the String with which to quote the list elements * @param s the array of String array objects */ public static String getList(String[][] s, String separator, String quote) { int len = s.length; StringBuffer sb = new StringBuffer(len * 16); for (int i = 0; i < len; i++) { sb.append(quote); sb.append(s[i][0]); sb.append(quote); if (i + 1 < len) { sb.append(separator); } } return sb.toString(); } } </source>

          Csv Converter

             <source lang="java">
           
          

          /*

          * 
          * Copyright (c) 2004 SourceTap - www.sourcetap.ru
          *
          *  The contents of this file are subject to the SourceTap Public License 
          * ("License"); You may not use this file except in compliance with the 
          * License. You may obtain a copy of the License at http://www.sourcetap.ru/license.htm
          * Software distributed under the License is distributed on an  "AS IS"  basis,
          * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
          * the specific language governing rights and limitations under the License.
          *
          * The above copyright notice and this permission notice shall be included
          * in all copies or substantial portions of the Software.
          *
          */
          

          import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer;

          /**

          *
          * @author  Administrator
          * @version
          */
          

          public class CsvConverter {

             private static String DELIM = ",";
             private String[] headers = null;
             private ArrayList data = new ArrayList();
             public CsvConverter(Reader in) {
                 String line = "";
                 boolean doHeader = true;
                 StringTokenizer st = null;
                 try {
                     BufferedReader br = new BufferedReader(in);
                     while ((line = br.readLine()) != null) {
                         if (line == null) {
                             throw new IOException("Empty Data Source");
                         }
                         if (doHeader) {
                             headers = breakCSVStringApart(line);
                             doHeader = false;
                         } else {
                             String[] rowArray = breakCSVStringApart(line);
                             if ((rowArray.length < headers.length) &&
                                     (rowArray.length < 2)) {
                                 //skip as blank row
                             } else {
                                 data.add(rowArray);
                             }
                         }
                     }
                 } catch (IOException e) {
                     e.printStackTrace();
                 } finally {
                     try {
                         in.close();
                     } catch (Exception e) {
                         ;
                     }
                 }
             }
             /**
              * DOCUMENT ME!
              *
              * @return 
              *
              * @throws IOException 
              */
             public String[] getHeader() throws IOException {
                 return headers;
             }
             /**
              * DOCUMENT ME!
              *
              * @return 
              *
              * @throws IOException 
              */
             public ArrayList getData() throws IOException {
                 return data;
             }
             /**
              * DOCUMENT ME!
              *
              * @param fileName 
              */
             public void writeToFile(String fileName) {
                 try {
                     FileWriter bwOut = new FileWriter(fileName);
                     //write headers
                     for (int i = 0; i < headers.length; i++) {
                         bwOut.write(createCSVField(headers[i]));
                         if (i != (headers.length - 1)) {
                             bwOut.write(",");
                         }
                     }
                     bwOut.write("\n");
                     //write data
                     for (int i = 0; i < data.size(); i++) {
                         String[] dataArray = (String[]) data.get(i);
                         for (int j = 0; j < dataArray.length; j++) {
                             bwOut.write(createCSVField(dataArray[j]));
                             if (j != (dataArray.length - 1)) {
                                 bwOut.write(",");
                             }
                         }
                         bwOut.write("\n");
                     }
                     bwOut.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             /**
              * DOCUMENT ME!
              *
              * @param in 
              *
              * @return 
              */
             public String[] breakCSVStringApart(String in) {
                 StringBuffer curString = new StringBuffer();
                 List strings = new ArrayList();
                 boolean escaped = false;
                 boolean inquotedstring = false;
                 for (int i = 0; i < in.length(); i++) {
                     char c = in.charAt(i);
                     switch (c) {
                     case ",":
                         if (inquotedstring) {
                             curString.append(",");
                         } else {
                             strings.add(curString.toString().trim());
                             curString = new StringBuffer();
                         }
                     case " ":
                         // end word
                         //if (inquotedstring) {
                         curString.append(" ");
                         //}
                         break;
                     case "\t":
                         // end word
                         if (inquotedstring) {
                             curString.append("\t");
                         }
                         break;
                     case """:
                         if (escaped) {
                             curString.append(""");
                             escaped = false;
                         } else if (inquotedstring) {
                             inquotedstring = false;
                             //strings.add(curString.toString());
                             //curString = new StringBuffer();
                         } else {
                             inquotedstring = true;
                         }
                         break;
                     case "\\":
                         if (escaped) {
                             curString.append("\\");
                             escaped = false;
                         } else {
                             escaped = true;
                         }
                         break;
                     default:
                         if (escaped) {
                             switch (c) {
                             case "n":
                                 curString.append("\n");
                                 break;
                             case "t":
                                 curString.append("\t");
                                 break;
                             case "r":
                                 curString.append("\r");
                                 break;
                             default:
                                 break;
                             }
                             escaped = false;
                         } else {
                             curString.append(c);
                         }
                     }
                 }
                 if (curString.length() > 0) {
                     strings.add(curString.toString().trim());
                 }
                 return (String[]) strings.toArray(new String[0]);
             }
             /**
              * DOCUMENT ME!
              *
              * @param in 
              *
              * @return 
              */
             public static String createCSVField(String in) {
                 StringBuffer curString = new StringBuffer();
                 boolean needsQuotes = false;
                 for (int i = 0; i < in.length(); i++) {
                     char c = in.charAt(i);
                     switch (c) {
                     case "\n":
                         curString.append("\\n");
                         break;
                     case "\t":
                         curString.append("\\t");
                         break;
                     case "\r":
                         curString.append("\\r");
                         break;
                     case ",":
                         curString.append(",");
                         needsQuotes = true;
                         break;
                     case "\\":
                         curString.append("\\\\");
                         break;
                     case """:
                         curString.append("\\\"");
                         break;
                     default:
                         curString.append(c);
                         break;
                     }
                 }
                 if (needsQuotes) {
                     return "\"" + curString.toString() + "\"";
                 } else {
                     return curString.toString();
                 }
             }
          

          }


           </source>
             
            
           
            
          



          CSV in action: lines from a file and print

             <source lang="java">
           
          

          /*

          * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
          * All rights reserved. Software written by Ian F. Darwin and others.
          * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
          *
          * Redistribution and use in source and binary forms, with or without
          * modification, are permitted provided that the following conditions
          * are met:
          * 1. Redistributions of source code must retain the above copyright
          *    notice, this list of conditions and the following disclaimer.
          * 2. Redistributions in binary form must reproduce the above copyright
          *    notice, this list of conditions and the following disclaimer in the
          *    documentation and/or other materials provided with the distribution.
          *
          * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
          * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
          * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
          * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
          * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
          * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
          * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
          * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
          * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
          * POSSIBILITY OF SUCH DAMAGE.
          * 
          * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
          * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
          * pioneering role in inventing and promulgating (and standardizing) the Java 
          * language and environment is gratefully acknowledged.
          * 
          * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
          * inventing predecessor languages C and C++ is also gratefully acknowledged.
          */
          

          import java.io.*; import java.util.*; /** CSV in action: lines from a file and print. */ public class CSVFile {

           public static void main(String[] args) throws IOException {
           
             // Construct a new CSV parser.
             CSV csv = new CSV();
             if (args.length == 0) {  // read standard input
               BufferedReader is = new BufferedReader(
                 new InputStreamReader(System.in));
               process(csv, is);
             } else {
               for (int i=0; i<args.length; i++) {
                 process(csv, new BufferedReader(new FileReader(args[i])));
               }
             }
           }
           protected static void process(CSV csv, BufferedReader is) throws IOException {
             String line;
             while ((line = is.readLine()) != null) {
               System.out.println("line = `" + line + """);
               Iterator e = csv.parse(line).iterator();
               int i = 0;
               while (e.hasNext()) 
                 System.out.println("field[" + i++ + "] = `" +
                   e.next() + """);
             }
           }
          

          } /** Parse comma-separated values (CSV), a common Windows file format.

          * Sample input: "LU",86.25,"11/4/1998","2:19PM",+4.0625
          * <p>
          * Inner logic adapted from a C++ original that was
          * Copyright (C) 1999 Lucent Technologies
          * Excerpted from "The Practice of Programming"
          * by Brian W. Kernighan and Rob Pike.
          * <p>
          * Included by permission of the http://tpop.awl.ru/ web site, 
          * which says:
          * "You may use this code for any purpose, as long as you leave 
          * the copyright notice and book citation attached." I have done so.
          * @author Brian W. Kernighan and Rob Pike (C++ original)
          * @author Ian F. Darwin (translation into Java and removal of I/O)
          * @author Ben Ballard (rewrote advQuoted to handle """" and for readability)
          */
          

          class CSV {

           public static final char DEFAULT_SEP = ",";
           /** Construct a CSV parser, with the default separator (`,"). */
           public CSV() {
             this(DEFAULT_SEP);
           }
           /** Construct a CSV parser with a given separator. 
            * @param sep The single char for the separator (not a list of
            * separator characters)
            */
           public CSV(char sep) {
             fieldSep = sep;
           }
           /** The fields in the current String */
           protected List list = new ArrayList();
           /** the separator char for this parser */
           protected char fieldSep;
           /** parse: break the input String into fields
            * @return java.util.Iterator containing each field 
            * from the original as a String, in order.
            */
           public List parse(String line)
           {
             StringBuffer sb = new StringBuffer();
             list.clear();      // recycle to initial state
             int i = 0;
             if (line.length() == 0) {
               list.add(line);
               return list;
             }
             do {
                     sb.setLength(0);
                     if (i < line.length() && line.charAt(i) == """)
                         i = advQuoted(line, sb, ++i);  // skip quote
                     else
                         i = advPlain(line, sb, i);
                     list.add(sb.toString());
               i++;
             } while (i < line.length());
             return list;
           }
           /** advQuoted: quoted field; return index of next separator */
           protected int advQuoted(String s, StringBuffer sb, int i)
           {
             int j;
             int len= s.length();
                 for (j=i; j<len; j++) {
                     if (s.charAt(j) == """ && j+1 < len) {
                         if (s.charAt(j+1) == """) {
                             j++; // skip escape char
                         } else if (s.charAt(j+1) == fieldSep) { //next delimeter
                             j++; // skip end quotes
                             break;
                         }
                     } else if (s.charAt(j) == """ && j+1 == len) { // end quotes at end of line
                         break; //done
               }
               sb.append(s.charAt(j));  // regular character.
             }
             return j;
           }
           /** advPlain: unquoted field; return index of next separator */
           protected int advPlain(String s, StringBuffer sb, int i)
           {
             int j;
             j = s.indexOf(fieldSep, i); // look for separator
                 if (j == -1) {                 // none found
                     sb.append(s.substring(i));
                     return s.length();
                 } else {
                     sb.append(s.substring(i, j));
                     return j;
                 }
             }
          

          } //File:CSV.dat /* "Hello","World" 123,456 "LU",86.25|"11/4/1998"|"2:19PM"|+4.0625 bad "input",123e01 XYZZY,""|"OReilly & Associates| Inc."|"Darwin| Ian"|"a \"glug\" bit|"|5|"Memory fault| core NOT dumped"

          • /



           </source>
             
            
           
            
          



          CSV parser

          Csv Reader

             <source lang="java">
          
          

          /*

          * Java CSV is a stream based library for reading and writing
          * CSV and other delimited data.
          *   
          * Copyright (C) Bruce Dunwiddie bruce@csvreader.ru
          *
          * This library is free software; you can redistribute it and/or
          * modify it under the terms of the GNU Lesser General Public
          * License as published by the Free Software Foundation; either
          * version 2.1 of the License, or (at your option) any later version.
          *
          * This library is distributed in the hope that it will be useful,
          * but WITHOUT ANY WARRANTY; without even the implied warranty of
          * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
          * Lesser General Public License for more details.
          *
          * You should have received a copy of the GNU Lesser General Public
          * License along with this library; if not, write to the Free Software
          * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
          */
          

          import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.nio.charset.Charset; import java.text.NumberFormat; import java.util.HashMap; /**

          * A stream based parser for parsing delimited text data from a file or a
          * stream.
          */
          

          public class CsvReader {

           private Reader inputStream = null;
           private String fileName = null;
           // this holds all the values for switches that the user is allowed to set
           private UserSettings userSettings = new UserSettings();
           private Charset charset = null;
           private boolean useCustomRecordDelimiter = false;
           // this will be our working buffer to hold data chunks
           // read in from the data file
           private DataBuffer dataBuffer = new DataBuffer();
           private ColumnBuffer columnBuffer = new ColumnBuffer();
           private RawRecordBuffer rawBuffer = new RawRecordBuffer();
           private boolean[] isQualified = null;
           private String rawRecord = "";
           private HeadersHolder headersHolder = new HeadersHolder();
           // these are all more or less global loop variables
           // to keep from needing to pass them all into various
           // methods during parsing
           private boolean startedColumn = false;
           private boolean startedWithQualifier = false;
           private boolean hasMoreData = true;
           private char lastLetter = "\0";
           private boolean hasReadNextLine = false;
           private int columnsCount = 0;
           private long currentRecord = 0;
           private String[] values = new String[StaticSettings.INITIAL_COLUMN_COUNT];
           private boolean initialized = false;
           private boolean closed = false;
           /**
            * Double up the text qualifier to represent an occurance of the text
            * qualifier.
            */
           public static final int ESCAPE_MODE_DOUBLED = 1;
           /**
            * Use a backslash character before the text qualifier to represent an
            * occurance of the text qualifier.
            */
           public static final int ESCAPE_MODE_BACKSLASH = 2;
           /**
            * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
            * as the data source.
            * 
            * @param fileName
            *            The path to the file to use as the data source.
            * @param delimiter
            *            The character to use as the column delimiter.
            * @param charset
            *            The {@link java.nio.charset.Charset Charset} to use while
            *            parsing the data.
            */
           public CsvReader(String fileName, char delimiter, Charset charset)
               throws FileNotFoundException {
             if (fileName == null) {
               throw new IllegalArgumentException(
                   "Parameter fileName can not be null.");
             }
             if (charset == null) {
               throw new IllegalArgumentException(
                   "Parameter charset can not be null.");
             }
             if (!new File(fileName).exists()) {
               throw new FileNotFoundException("File " + fileName
                   + " does not exist.");
             }
             this.fileName = fileName;
             this.userSettings.Delimiter = delimiter;
             this.charset = charset;
             isQualified = new boolean[values.length];
           }
           /**
            * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
            * as the data source. Uses ISO-8859-1 as the
            * {@link java.nio.charset.Charset Charset}.
            * 
            * @param fileName
            *            The path to the file to use as the data source.
            * @param delimiter
            *            The character to use as the column delimiter.
            */
           public CsvReader(String fileName, char delimiter)
               throws FileNotFoundException {
             this(fileName, delimiter, Charset.forName("ISO-8859-1"));
           }
           /**
            * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
            * as the data source. Uses a comma as the column delimiter and
            * ISO-8859-1 as the {@link java.nio.charset.Charset Charset}.
            * 
            * @param fileName
            *            The path to the file to use as the data source.
            */
           public CsvReader(String fileName) throws FileNotFoundException {
             this(fileName, Letters.ruMA);
           }
           /**
            * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
            * {@link java.io.Reader Reader} object as the data source.
            * 
            * @param inputStream
            *            The stream to use as the data source.
            * @param delimiter
            *            The character to use as the column delimiter.
            */
           public CsvReader(Reader inputStream, char delimiter) {
             if (inputStream == null) {
               throw new IllegalArgumentException(
                   "Parameter inputStream can not be null.");
             }
             this.inputStream = inputStream;
             this.userSettings.Delimiter = delimiter;
             initialized = true;
             isQualified = new boolean[values.length];
           }
           /**
            * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
            * {@link java.io.Reader Reader} object as the data source. Uses a
            * comma as the column delimiter.
            * 
            * @param inputStream
            *            The stream to use as the data source.
            */
           public CsvReader(Reader inputStream) {
             this(inputStream, Letters.ruMA);
           }
           /**
            * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
            * {@link java.io.InputStream InputStream} object as the data source.
            * 
            * @param inputStream
            *            The stream to use as the data source.
            * @param delimiter
            *            The character to use as the column delimiter.
            * @param charset
            *            The {@link java.nio.charset.Charset Charset} to use while
            *            parsing the data.
            */
           public CsvReader(InputStream inputStream, char delimiter, Charset charset) {
             this(new InputStreamReader(inputStream, charset), delimiter);
           }
           /**
            * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
            * {@link java.io.InputStream InputStream} object as the data
            * source. Uses a comma as the column delimiter.
            * 
            * @param inputStream
            *            The stream to use as the data source.
            * @param charset
            *            The {@link java.nio.charset.Charset Charset} to use while
            *            parsing the data.
            */
           public CsvReader(InputStream inputStream, Charset charset) {
             this(new InputStreamReader(inputStream, charset));
           }
           public boolean getCaptureRawRecord() {
             return userSettings.CaptureRawRecord;
           }
           public void setCaptureRawRecord(boolean captureRawRecord) {
             userSettings.CaptureRawRecord = captureRawRecord;
           }
           public String getRawRecord() {
             return rawRecord;
           }
           /**
            * Gets whether leading and trailing whitespace characters are being trimmed
            * from non-textqualified column data. Default is true.
            * 
            * @return Whether leading and trailing whitespace characters are being
            *         trimmed from non-textqualified column data.
            */
           public boolean getTrimWhitespace() {
             return userSettings.TrimWhitespace;
           }
           /**
            * Sets whether leading and trailing whitespace characters should be trimmed
            * from non-textqualified column data or not. Default is true.
            * 
            * @param trimWhitespace
            *            Whether leading and trailing whitespace characters should be
            *            trimmed from non-textqualified column data or not.
            */
           public void setTrimWhitespace(boolean trimWhitespace) {
             userSettings.TrimWhitespace = trimWhitespace;
           }
           /**
            * Gets the character being used as the column delimiter. Default is comma,
            * ",".
            * 
            * @return The character being used as the column delimiter.
            */
           public char getDelimiter() {
             return userSettings.Delimiter;
           }
           /**
            * Sets the character to use as the column delimiter. Default is comma, ",".
            * 
            * @param delimiter
            *            The character to use as the column delimiter.
            */
           public void setDelimiter(char delimiter) {
             userSettings.Delimiter = delimiter;
           }
           public char getRecordDelimiter() {
             return userSettings.RecordDelimiter;
           }
           /**
            * Sets the character to use as the record delimiter.
            * 
            * @param recordDelimiter
            *            The character to use as the record delimiter. Default is
            *            combination of standard end of line characters for Windows,
            *            Unix, or Mac.
            */
           public void setRecordDelimiter(char recordDelimiter) {
             useCustomRecordDelimiter = true;
             userSettings.RecordDelimiter = recordDelimiter;
           }
           /**
            * Gets the character to use as a text qualifier in the data.
            * 
            * @return The character to use as a text qualifier in the data.
            */
           public char getTextQualifier() {
             return userSettings.TextQualifier;
           }
           /**
            * Sets the character to use as a text qualifier in the data.
            * 
            * @param textQualifier
            *            The character to use as a text qualifier in the data.
            */
           public void setTextQualifier(char textQualifier) {
             userSettings.TextQualifier = textQualifier;
           }
           /**
            * Whether text qualifiers will be used while parsing or not.
            * 
            * @return Whether text qualifiers will be used while parsing or not.
            */
           public boolean getUseTextQualifier() {
             return userSettings.UseTextQualifier;
           }
           /**
            * Sets whether text qualifiers will be used while parsing or not.
            * 
            * @param useTextQualifier
            *            Whether to use a text qualifier while parsing or not.
            */
           public void setUseTextQualifier(boolean useTextQualifier) {
             userSettings.UseTextQualifier = useTextQualifier;
           }
           /**
            * Gets the character being used as a comment signal.
            * 
            * @return The character being used as a comment signal.
            */
           public char getComment() {
             return userSettings.rument;
           }
           /**
            * Sets the character to use as a comment signal.
            * 
            * @param comment
            *            The character to use as a comment signal.
            */
           public void setComment(char comment) {
             userSettings.rument = comment;
           }
           /**
            * Gets whether comments are being looked for while parsing or not.
            * 
            * @return Whether comments are being looked for while parsing or not.
            */
           public boolean getUseComments() {
             return userSettings.UseComments;
           }
           /**
            * Sets whether comments are being looked for while parsing or not.
            * 
            * @param useComments
            *            Whether comments are being looked for while parsing or not.
            */
           public void setUseComments(boolean useComments) {
             userSettings.UseComments = useComments;
           }
           /**
            * Gets the current way to escape an occurance of the text qualifier inside
            * qualified data.
            * 
            * @return The current way to escape an occurance of the text qualifier
            *         inside qualified data.
            */
           public int getEscapeMode() {
             return userSettings.EscapeMode;
           }
           /**
            * Sets the current way to escape an occurance of the text qualifier inside
            * qualified data.
            * 
            * @param escapeMode
            *            The way to escape an occurance of the text qualifier inside
            *            qualified data.
            * @exception IllegalArgumentException
            *                When an illegal value is specified for escapeMode.
            */
           public void setEscapeMode(int escapeMode) throws IllegalArgumentException {
             if (escapeMode != ESCAPE_MODE_DOUBLED
                 && escapeMode != ESCAPE_MODE_BACKSLASH) {
               throw new IllegalArgumentException(
                   "Parameter escapeMode must be a valid value.");
             }
             userSettings.EscapeMode = escapeMode;
           }
           public boolean getSkipEmptyRecords() {
             return userSettings.SkipEmptyRecords;
           }
           public void setSkipEmptyRecords(boolean skipEmptyRecords) {
             userSettings.SkipEmptyRecords = skipEmptyRecords;
           }
           /**
            * Safety caution to prevent the parser from using large amounts of memory
            * in the case where parsing settings like file encodings don"t end up
            * matching the actual format of a file. This switch can be turned off if
            * the file format is known and tested. With the switch off, the max column
            * lengths and max column count per record supported by the parser will
            * greatly increase. Default is true.
            * 
            * @return The current setting of the safety switch.
            */
           public boolean getSafetySwitch() {
             return userSettings.SafetySwitch;
           }
           /**
            * Safety caution to prevent the parser from using large amounts of memory
            * in the case where parsing settings like file encodings don"t end up
            * matching the actual format of a file. This switch can be turned off if
            * the file format is known and tested. With the switch off, the max column
            * lengths and max column count per record supported by the parser will
            * greatly increase. Default is true.
            * 
            * @param safetySwitch
            */
           public void setSafetySwitch(boolean safetySwitch) {
             userSettings.SafetySwitch = safetySwitch;
           }
           /**
            * Gets the count of columns found in this record.
            * 
            * @return The count of columns found in this record.
            */
           public int getColumnCount() {
             return columnsCount;
           }
           /**
            * Gets the index of the current record.
            * 
            * @return The index of the current record.
            */
           public long getCurrentRecord() {
             return currentRecord - 1;
           }
           /**
            * Gets the count of headers read in by a previous call to
            * {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
            * 
            * @return The count of headers read in by a previous call to
            *         {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
            */
           public int getHeaderCount() {
             return headersHolder.Length;
           }
           /**
            * Returns the header values as a string array.
            * 
            * @return The header values as a String array.
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           public String[] getHeaders() throws IOException {
             checkClosed();
             if (headersHolder.Headers == null) {
               return null;
             } else {
               // use clone here to prevent the outside code from
               // setting values on the array directly, which would
               // throw off the index lookup based on header name
               String[] clone = new String[headersHolder.Length];
               System.arraycopy(headersHolder.Headers, 0, clone, 0,
                   headersHolder.Length);
               return clone;
             }
           }
           public void setHeaders(String[] headers) {
             headersHolder.Headers = headers;
             headersHolder.IndexByName.clear();
             if (headers != null) {
               headersHolder.Length = headers.length;
             } else {
               headersHolder.Length = 0;
             }
             // use headersHolder.Length here in case headers is null
             for (int i = 0; i < headersHolder.Length; i++) {
               headersHolder.IndexByName.put(headers[i], Integer.valueOf(i));
             }
           }
           public String[] getValues() throws IOException {
             checkClosed();
             // need to return a clone, and can"t use clone because values.Length
             // might be greater than columnsCount
             String[] clone = new String[columnsCount];
             System.arraycopy(values, 0, clone, 0, columnsCount);
             return clone;
           }
           /**
            * Returns the current column value for a given column index.
            * 
            * @param columnIndex
            *            The index of the column.
            * @return The current column value.
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           public String get(int columnIndex) throws IOException {
             checkClosed();
             if (columnIndex > -1 && columnIndex < columnsCount) {
               return values[columnIndex];
             } else {
               return "";
             }
           }
           /**
            * Returns the current column value for a given column header name.
            * 
            * @param headerName
            *            The header name of the column.
            * @return The current column value.
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           public String get(String headerName) throws IOException {
             checkClosed();
             return get(getIndex(headerName));
           }
           /**
            * Creates a {@link com.csvreader.CsvReader CsvReader} object using a string
            * of data as the source. Uses ISO-8859-1 as the
            * {@link java.nio.charset.Charset Charset}.
            * 
            * @param data
            *            The String of data to use as the source.
            * @return A {@link com.csvreader.CsvReader CsvReader} object using the
            *         String of data as the source.
            */
           public static CsvReader parse(String data) {
             if (data == null) {
               throw new IllegalArgumentException(
                   "Parameter data can not be null.");
             }
             return new CsvReader(new StringReader(data));
           }
           /**
            * Reads another record.
            * 
            * @return Whether another record was successfully read or not.
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           public boolean readRecord() throws IOException {
             checkClosed();
             columnsCount = 0;
             rawBuffer.Position = 0;
             dataBuffer.LineStart = dataBuffer.Position;
             hasReadNextLine = false;
             // check to see if we"ve already found the end of data
             if (hasMoreData) {
               // loop over the data stream until the end of data is found
               // or the end of the record is found
               do {
                 if (dataBuffer.Position == dataBuffer.Count) {
                   checkDataLength();
                 } else {
                   startedWithQualifier = false;
                   // grab the current letter as a char
                   char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
                   if (userSettings.UseTextQualifier
                       && currentLetter == userSettings.TextQualifier) {
                     // this will be a text qualified column, so
                     // we need to set startedWithQualifier to make it
                     // enter the seperate branch to handle text
                     // qualified columns
                     lastLetter = currentLetter;
                     // read qualified
                     startedColumn = true;
                     dataBuffer.ColumnStart = dataBuffer.Position + 1;
                     startedWithQualifier = true;
                     boolean lastLetterWasQualifier = false;
                     char escapeChar = userSettings.TextQualifier;
                     if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH) {
                       escapeChar = Letters.BACKSLASH;
                     }
                     boolean eatingTrailingJunk = false;
                     boolean lastLetterWasEscape = false;
                     boolean readingComplexEscape = false;
                     int escape = ComplexEscape.UNICODE;
                     int escapeLength = 0;
                     char escapeValue = (char) 0;
                     dataBuffer.Position++;
                     do {
                       if (dataBuffer.Position == dataBuffer.Count) {
                         checkDataLength();
                       } else {
                         // grab the current letter as a char
                         currentLetter = dataBuffer.Buffer[dataBuffer.Position];
                         if (eatingTrailingJunk) {
                           dataBuffer.ColumnStart = dataBuffer.Position + 1;
                           if (currentLetter == userSettings.Delimiter) {
                             endColumn();
                           } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                               || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                             endColumn();
                             endRecord();
                           }
                         } else if (readingComplexEscape) {
                           escapeLength++;
                           switch (escape) {
                           case ComplexEscape.UNICODE:
                             escapeValue *= (char) 16;
                             escapeValue += hexToDec(currentLetter);
                             if (escapeLength == 4) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.OCTAL:
                             escapeValue *= (char) 8;
                             escapeValue += (char) (currentLetter - "0");
                             if (escapeLength == 3) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.DECIMAL:
                             escapeValue *= (char) 10;
                             escapeValue += (char) (currentLetter - "0");
                             if (escapeLength == 3) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.HEX:
                             escapeValue *= (char) 16;
                             escapeValue += hexToDec(currentLetter);
                             if (escapeLength == 2) {
                               readingComplexEscape = false;
                             }
                             break;
                           }
                           if (!readingComplexEscape) {
                             appendLetter(escapeValue);
                           } else {
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                           }
                         } else if (currentLetter == userSettings.TextQualifier) {
                           if (lastLetterWasEscape) {
                             lastLetterWasEscape = false;
                             lastLetterWasQualifier = false;
                           } else {
                             updateCurrentValue();
                             if (userSettings.EscapeMode == ESCAPE_MODE_DOUBLED) {
                               lastLetterWasEscape = true;
                             }
                             lastLetterWasQualifier = true;
                           }
                         } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                             && lastLetterWasEscape) {
                           switch (currentLetter) {
                           case "n":
                             appendLetter(Letters.LF);
                             break;
                           case "r":
                             appendLetter(Letters.CR);
                             break;
                           case "t":
                             appendLetter(Letters.TAB);
                             break;
                           case "b":
                             appendLetter(Letters.BACKSPACE);
                             break;
                           case "f":
                             appendLetter(Letters.FORM_FEED);
                             break;
                           case "e":
                             appendLetter(Letters.ESCAPE);
                             break;
                           case "v":
                             appendLetter(Letters.VERTICAL_TAB);
                             break;
                           case "a":
                             appendLetter(Letters.ALERT);
                             break;
                           case "0":
                           case "1":
                           case "2":
                           case "3":
                           case "4":
                           case "5":
                           case "6":
                           case "7":
                             escape = ComplexEscape.OCTAL;
                             readingComplexEscape = true;
                             escapeLength = 1;
                             escapeValue = (char) (currentLetter - "0");
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                             break;
                           case "u":
                           case "x":
                           case "o":
                           case "d":
                           case "U":
                           case "X":
                           case "O":
                           case "D":
                             switch (currentLetter) {
                             case "u":
                             case "U":
                               escape = ComplexEscape.UNICODE;
                               break;
                             case "x":
                             case "X":
                               escape = ComplexEscape.HEX;
                               break;
                             case "o":
                             case "O":
                               escape = ComplexEscape.OCTAL;
                               break;
                             case "d":
                             case "D":
                               escape = ComplexEscape.DECIMAL;
                               break;
                             }
                             readingComplexEscape = true;
                             escapeLength = 0;
                             escapeValue = (char) 0;
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                             break;
                           default:
                             break;
                           }
                           lastLetterWasEscape = false;
                           // can only happen for ESCAPE_MODE_BACKSLASH
                         } else if (currentLetter == escapeChar) {
                           updateCurrentValue();
                           lastLetterWasEscape = true;
                         } else {
                           if (lastLetterWasQualifier) {
                             if (currentLetter == userSettings.Delimiter) {
                               endColumn();
                             } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                                 || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                               endColumn();
                               endRecord();
                             } else {
                               dataBuffer.ColumnStart = dataBuffer.Position + 1;
                               eatingTrailingJunk = true;
                             }
                             // make sure to clear the flag for next
                             // run of the loop
                             lastLetterWasQualifier = false;
                           }
                         }
                         // keep track of the last letter because we need
                         // it for several key decisions
                         lastLetter = currentLetter;
                         if (startedColumn) {
                           dataBuffer.Position++;
                           if (userSettings.SafetySwitch
                               && dataBuffer.Position
                                   - dataBuffer.ColumnStart
                                   + columnBuffer.Position > 100000) {
                             close();
                             throw new IOException(
                                 "Maximum column length of 100,000 exceeded in column "
                                     + NumberFormat
                                         .getIntegerInstance()
                                         .format(
                                             columnsCount)
                                     + " in record "
                                     + NumberFormat
                                         .getIntegerInstance()
                                         .format(
                                             currentRecord)
                                     + ". Set the SafetySwitch property to false"
                                     + " if you"re expecting column lengths greater than 100,000 characters to"
                                     + " avoid this error.");
                           }
                         }
                       } // end else
                     } while (hasMoreData && startedColumn);
                   } else if (currentLetter == userSettings.Delimiter) {
                     // we encountered a column with no data, so
                     // just send the end column
                     lastLetter = currentLetter;
                     endColumn();
                   } else if (useCustomRecordDelimiter
                       && currentLetter == userSettings.RecordDelimiter) {
                     // this will skip blank lines
                     if (startedColumn || columnsCount > 0
                         || !userSettings.SkipEmptyRecords) {
                       endColumn();
                       endRecord();
                     } else {
                       dataBuffer.LineStart = dataBuffer.Position + 1;
                     }
                     lastLetter = currentLetter;
                   } else if (!useCustomRecordDelimiter
                       && (currentLetter == Letters.CR || currentLetter == Letters.LF)) {
                     // this will skip blank lines
                     if (startedColumn
                         || columnsCount > 0
                         || (!userSettings.SkipEmptyRecords && (currentLetter == Letters.CR || lastLetter != Letters.CR))) {
                       endColumn();
                       endRecord();
                     } else {
                       dataBuffer.LineStart = dataBuffer.Position + 1;
                     }
                     lastLetter = currentLetter;
                   } else if (userSettings.UseComments && columnsCount == 0
                       && currentLetter == userSettings.rument) {
                     // encountered a comment character at the beginning of
                     // the line so just ignore the rest of the line
                     lastLetter = currentLetter;
                     skipLine();
                   } else if (userSettings.TrimWhitespace
                       && (currentLetter == Letters.SPACE || currentLetter == Letters.TAB)) {
                     // do nothing, this will trim leading whitespace
                     // for both text qualified columns and non
                     startedColumn = true;
                     dataBuffer.ColumnStart = dataBuffer.Position + 1;
                   } else {
                     // since the letter wasn"t a special letter, this
                     // will be the first letter of our current column
                     startedColumn = true;
                     dataBuffer.ColumnStart = dataBuffer.Position;
                     boolean lastLetterWasBackslash = false;
                     boolean readingComplexEscape = false;
                     int escape = ComplexEscape.UNICODE;
                     int escapeLength = 0;
                     char escapeValue = (char) 0;
                     boolean firstLoop = true;
                     do {
                       if (!firstLoop
                           && dataBuffer.Position == dataBuffer.Count) {
                         checkDataLength();
                       } else {
                         if (!firstLoop) {
                           // grab the current letter as a char
                           currentLetter = dataBuffer.Buffer[dataBuffer.Position];
                         }
                         if (!userSettings.UseTextQualifier
                             && userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                             && currentLetter == Letters.BACKSLASH) {
                           if (lastLetterWasBackslash) {
                             lastLetterWasBackslash = false;
                           } else {
                             updateCurrentValue();
                             lastLetterWasBackslash = true;
                           }
                         } else if (readingComplexEscape) {
                           escapeLength++;
                           switch (escape) {
                           case ComplexEscape.UNICODE:
                             escapeValue *= (char) 16;
                             escapeValue += hexToDec(currentLetter);
                             if (escapeLength == 4) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.OCTAL:
                             escapeValue *= (char) 8;
                             escapeValue += (char) (currentLetter - "0");
                             if (escapeLength == 3) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.DECIMAL:
                             escapeValue *= (char) 10;
                             escapeValue += (char) (currentLetter - "0");
                             if (escapeLength == 3) {
                               readingComplexEscape = false;
                             }
                             break;
                           case ComplexEscape.HEX:
                             escapeValue *= (char) 16;
                             escapeValue += hexToDec(currentLetter);
                             if (escapeLength == 2) {
                               readingComplexEscape = false;
                             }
                             break;
                           }
                           if (!readingComplexEscape) {
                             appendLetter(escapeValue);
                           } else {
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                           }
                         } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
                             && lastLetterWasBackslash) {
                           switch (currentLetter) {
                           case "n":
                             appendLetter(Letters.LF);
                             break;
                           case "r":
                             appendLetter(Letters.CR);
                             break;
                           case "t":
                             appendLetter(Letters.TAB);
                             break;
                           case "b":
                             appendLetter(Letters.BACKSPACE);
                             break;
                           case "f":
                             appendLetter(Letters.FORM_FEED);
                             break;
                           case "e":
                             appendLetter(Letters.ESCAPE);
                             break;
                           case "v":
                             appendLetter(Letters.VERTICAL_TAB);
                             break;
                           case "a":
                             appendLetter(Letters.ALERT);
                             break;
                           case "0":
                           case "1":
                           case "2":
                           case "3":
                           case "4":
                           case "5":
                           case "6":
                           case "7":
                             escape = ComplexEscape.OCTAL;
                             readingComplexEscape = true;
                             escapeLength = 1;
                             escapeValue = (char) (currentLetter - "0");
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                             break;
                           case "u":
                           case "x":
                           case "o":
                           case "d":
                           case "U":
                           case "X":
                           case "O":
                           case "D":
                             switch (currentLetter) {
                             case "u":
                             case "U":
                               escape = ComplexEscape.UNICODE;
                               break;
                             case "x":
                             case "X":
                               escape = ComplexEscape.HEX;
                               break;
                             case "o":
                             case "O":
                               escape = ComplexEscape.OCTAL;
                               break;
                             case "d":
                             case "D":
                               escape = ComplexEscape.DECIMAL;
                               break;
                             }
                             readingComplexEscape = true;
                             escapeLength = 0;
                             escapeValue = (char) 0;
                             dataBuffer.ColumnStart = dataBuffer.Position + 1;
                             break;
                           default:
                             break;
                           }
                           lastLetterWasBackslash = false;
                         } else {
                           if (currentLetter == userSettings.Delimiter) {
                             endColumn();
                           } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
                               || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
                             endColumn();
                             endRecord();
                           }
                         }
                         // keep track of the last letter because we need
                         // it for several key decisions
                         lastLetter = currentLetter;
                         firstLoop = false;
                         if (startedColumn) {
                           dataBuffer.Position++;
                           if (userSettings.SafetySwitch
                               && dataBuffer.Position
                                   - dataBuffer.ColumnStart
                                   + columnBuffer.Position > 100000) {
                             close();
                             throw new IOException(
                                 "Maximum column length of 100,000 exceeded in column "
                                     + NumberFormat
                                         .getIntegerInstance()
                                         .format(
                                             columnsCount)
                                     + " in record "
                                     + NumberFormat
                                         .getIntegerInstance()
                                         .format(
                                             currentRecord)
                                     + ". Set the SafetySwitch property to false"
                                     + " if you"re expecting column lengths greater than 100,000 characters to"
                                     + " avoid this error.");
                           }
                         }
                       } // end else
                     } while (hasMoreData && startedColumn);
                   }
                   if (hasMoreData) {
                     dataBuffer.Position++;
                   }
                 } // end else
               } while (hasMoreData && !hasReadNextLine);
               // check to see if we hit the end of the file
               // without processing the current record
               if (startedColumn || lastLetter == userSettings.Delimiter) {
                 endColumn();
                 endRecord();
               }
             }
             if (userSettings.CaptureRawRecord) {
               if (hasMoreData) {
                 if (rawBuffer.Position == 0) {
                   rawRecord = new String(dataBuffer.Buffer,
                       dataBuffer.LineStart, dataBuffer.Position
                           - dataBuffer.LineStart - 1);
                 } else {
                   rawRecord = new String(rawBuffer.Buffer, 0,
                       rawBuffer.Position)
                       + new String(dataBuffer.Buffer,
                           dataBuffer.LineStart, dataBuffer.Position
                               - dataBuffer.LineStart - 1);
                 }
               } else {
                 // for hasMoreData to ever be false, all data would have had to
                 // have been
                 // copied to the raw buffer
                 rawRecord = new String(rawBuffer.Buffer, 0, rawBuffer.Position);
               }
             } else {
               rawRecord = "";
             }
             return hasReadNextLine;
           }
           /**
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           private void checkDataLength() throws IOException {
             if (!initialized) {
               if (fileName != null) {
                 inputStream = new BufferedReader(new InputStreamReader(
                     new FileInputStream(fileName), charset),
                     StaticSettings.MAX_FILE_BUFFER_SIZE);
               }
               charset = null;
               initialized = true;
             }
             updateCurrentValue();
             if (userSettings.CaptureRawRecord && dataBuffer.Count > 0) {
               if (rawBuffer.Buffer.length - rawBuffer.Position < dataBuffer.Count
                   - dataBuffer.LineStart) {
                 int newLength = rawBuffer.Buffer.length
                     + Math.max(dataBuffer.Count - dataBuffer.LineStart,
                         rawBuffer.Buffer.length);
                 char[] holder = new char[newLength];
                 System.arraycopy(rawBuffer.Buffer, 0, holder, 0,
                     rawBuffer.Position);
                 rawBuffer.Buffer = holder;
               }
               System.arraycopy(dataBuffer.Buffer, dataBuffer.LineStart,
                   rawBuffer.Buffer, rawBuffer.Position, dataBuffer.Count
                       - dataBuffer.LineStart);
               rawBuffer.Position += dataBuffer.Count - dataBuffer.LineStart;
             }
             try {
               dataBuffer.Count = inputStream.read(dataBuffer.Buffer, 0,
                   dataBuffer.Buffer.length);
             } catch (IOException ex) {
               close();
               throw ex;
             }
             // if no more data could be found, set flag stating that
             // the end of the data was found
             if (dataBuffer.Count == -1) {
               hasMoreData = false;
             }
             dataBuffer.Position = 0;
             dataBuffer.LineStart = 0;
             dataBuffer.ColumnStart = 0;
           }
           /**
            * Read the first record of data as column headers.
            * 
            * @return Whether the header record was successfully read or not.
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           public boolean readHeaders() throws IOException {
             boolean result = readRecord();
             // copy the header data from the column array
             // to the header string array
             headersHolder.Length = columnsCount;
             headersHolder.Headers = new String[columnsCount];
             for (int i = 0; i < headersHolder.Length; i++) {
               String columnValue = get(i);
               headersHolder.Headers[i] = columnValue;
               // if there are duplicate header names, we will save the last one
               headersHolder.IndexByName.put(columnValue, Integer.valueOf(i));
             }
             if (result) {
               currentRecord--;
             }
             columnsCount = 0;
             return result;
           }
           /**
            * Returns the column header value for a given column index.
            * 
            * @param columnIndex
            *            The index of the header column being requested.
            * @return The value of the column header at the given column index.
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           public String getHeader(int columnIndex) throws IOException {
             checkClosed();
             // check to see if we have read the header record yet
             // check to see if the column index is within the bounds
             // of our header array
             if (columnIndex > -1 && columnIndex < headersHolder.Length) {
               // return the processed header data for this column
               return headersHolder.Headers[columnIndex];
             } else {
               return "";
             }
           }
           public boolean isQualified(int columnIndex) throws IOException {
             checkClosed();
             if (columnIndex < columnsCount && columnIndex > -1) {
               return isQualified[columnIndex];
             } else {
               return false;
             }
           }
           /**
            * @exception IOException
            *                Thrown if a very rare extreme exception occurs during
            *                parsing, normally resulting from improper data format.
            */
           private void endColumn() throws IOException {
             String currentValue = "";
             // must be called before setting startedColumn = false
             if (startedColumn) {
               if (columnBuffer.Position == 0) {
                 if (dataBuffer.ColumnStart < dataBuffer.Position) {
                   int lastLetter = dataBuffer.Position - 1;
                   if (userSettings.TrimWhitespace && !startedWithQualifier) {
                     while (lastLetter >= dataBuffer.ColumnStart
                         && (dataBuffer.Buffer[lastLetter] == Letters.SPACE || dataBuffer.Buffer[lastLetter] == Letters.TAB)) {
                       lastLetter--;
                     }
                   }
                   currentValue = new String(dataBuffer.Buffer,
                       dataBuffer.ColumnStart, lastLetter
                           - dataBuffer.ColumnStart + 1);
                 }
               } else {
                 updateCurrentValue();
                 int lastLetter = columnBuffer.Position - 1;
                 if (userSettings.TrimWhitespace && !startedWithQualifier) {
                   while (lastLetter >= 0
                       && (columnBuffer.Buffer[lastLetter] == Letters.SPACE || columnBuffer.Buffer[lastLetter] == Letters.SPACE)) {
                     lastLetter--;
                   }
                 }
                 currentValue = new String(columnBuffer.Buffer, 0,
                     lastLetter + 1);
               }
             }
             columnBuffer.Position = 0;
             startedColumn = false;
             if (columnsCount >= 100000 && userSettings.SafetySwitch) {
               close();
               throw new IOException(
                   "Maximum column count of 100,000 exceeded in record "
                       + NumberFormat.getIntegerInstance().format(
                           currentRecord)
                       + ". Set the SafetySwitch property to false"
                       + " if you"re expecting more than 100,000 columns per record to"
                       + " avoid this error.");
             }
             // check to see if our current holder array for
             // column chunks is still big enough to handle another
             // column chunk
             if (columnsCount == values.length) {
               // holder array needs to grow to be able to hold another column
               int newLength = values.length * 2;
               String[] holder = new String[newLength];
               System.arraycopy(values, 0, holder, 0, values.length);
               values = holder;
               boolean[] qualifiedHolder = new boolean[newLength];
               System.arraycopy(isQualified, 0, qualifiedHolder, 0,
                   isQualified.length);
               isQualified = qualifiedHolder;
             }
             values[columnsCount] = currentValue;
             isQualified[columnsCount] = startedWithQualifier;
             currentValue = "";
             columnsCount++;
           }
           private void appendLetter(char letter) {
             if (columnBuffer.Position == columnBuffer.Buffer.length) {
               int newLength = columnBuffer.Buffer.length * 2;
               char[] holder = new char[newLength];
               System.arraycopy(columnBuffer.Buffer, 0, holder, 0,
                   columnBuffer.Position);
               columnBuffer.Buffer = holder;
             }
             columnBuffer.Buffer[columnBuffer.Position++] = letter;
             dataBuffer.ColumnStart = dataBuffer.Position + 1;
           }
           private void updateCurrentValue() {
             if (startedColumn && dataBuffer.ColumnStart < dataBuffer.Position) {
               if (columnBuffer.Buffer.length - columnBuffer.Position < dataBuffer.Position
                   - dataBuffer.ColumnStart) {
                 int newLength = columnBuffer.Buffer.length
                     + Math.max(
                         dataBuffer.Position - dataBuffer.ColumnStart,
                         columnBuffer.Buffer.length);
                 char[] holder = new char[newLength];
                 System.arraycopy(columnBuffer.Buffer, 0, holder, 0,
                     columnBuffer.Position);
                 columnBuffer.Buffer = holder;
               }
               System.arraycopy(dataBuffer.Buffer, dataBuffer.ColumnStart,
                   columnBuffer.Buffer, columnBuffer.Position,
                   dataBuffer.Position - dataBuffer.ColumnStart);
               columnBuffer.Position += dataBuffer.Position
                   - dataBuffer.ColumnStart;
             }
             dataBuffer.ColumnStart = dataBuffer.Position + 1;
           }
           /**
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           private void endRecord() throws IOException {
             // this flag is used as a loop exit condition
             // during parsing
             hasReadNextLine = true;
             currentRecord++;
           }
           /**
            * Gets the corresponding column index for a given column header name.
            * 
            * @param headerName
            *            The header name of the column.
            * @return The column index for the given column header name. Returns
            *         -1 if not found.
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           public int getIndex(String headerName) throws IOException {
             checkClosed();
             Integer indexValue = headersHolder.IndexByName.get(headerName);
             if (indexValue != null) {
               return indexValue.intValue();
             } else {
               return -1;
             }
           }
           /**
            * Skips the next record of data by parsing each column. Does not
            * increment
            * {@link com.csvreader.CsvReader#getCurrentRecord getCurrentRecord()}.
            * 
            * @return Whether another record was successfully skipped or not.
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           public boolean skipRecord() throws IOException {
             checkClosed();
             boolean recordRead = false;
             if (hasMoreData) {
               recordRead = readRecord();
               if (recordRead) {
                 currentRecord--;
               }
             }
             return recordRead;
           }
           /**
            * Skips the next line of data using the standard end of line characters and
            * does not do any column delimited parsing.
            * 
            * @return Whether a line was successfully skipped or not.
            * @exception IOException
            *                Thrown if an error occurs while reading data from the
            *                source stream.
            */
           public boolean skipLine() throws IOException {
             checkClosed();
             // clear public column values for current line
             columnsCount = 0;
             boolean skippedLine = false;
             if (hasMoreData) {
               boolean foundEol = false;
               do {
                 if (dataBuffer.Position == dataBuffer.Count) {
                   checkDataLength();
                 } else {
                   skippedLine = true;
                   // grab the current letter as a char
                   char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
                   if (currentLetter == Letters.CR
                       || currentLetter == Letters.LF) {
                     foundEol = true;
                   }
                   // keep track of the last letter because we need
                   // it for several key decisions
                   lastLetter = currentLetter;
                   if (!foundEol) {
                     dataBuffer.Position++;
                   }
                 } // end else
               } while (hasMoreData && !foundEol);
               columnBuffer.Position = 0;
               dataBuffer.LineStart = dataBuffer.Position + 1;
             }
             rawBuffer.Position = 0;
             rawRecord = "";
             return skippedLine;
           }
           /**
            * Closes and releases all related resources.
            */
           public void close() {
             if (!closed) {
               close(true);
               closed = true;
             }
           }
           /**
            * 
            */
           private void close(boolean closing) {
             if (!closed) {
               if (closing) {
                 charset = null;
                 headersHolder.Headers = null;
                 headersHolder.IndexByName = null;
                 dataBuffer.Buffer = null;
                 columnBuffer.Buffer = null;
                 rawBuffer.Buffer = null;
               }
               try {
                 if (initialized) {
                   inputStream.close();
                 }
               } catch (Exception e) {
                 // just eat the exception
               }
               inputStream = null;
               closed = true;
             }
           }
           /**
            * @exception IOException
            *                Thrown if this object has already been closed.
            */
           private void checkClosed() throws IOException {
             if (closed) {
               throw new IOException(
                   "This instance of the CsvReader class has already been closed.");
             }
           }
           /**
            * 
            */
           protected void finalize() {
             close(false);
           }
           private class ComplexEscape {
             private static final int UNICODE = 1;
             private static final int OCTAL = 2;
             private static final int DECIMAL = 3;
             private static final int HEX = 4;
           }
           private static char hexToDec(char hex) {
             char result;
             if (hex >= "a") {
               result = (char) (hex - "a" + 10);
             } else if (hex >= "A") {
               result = (char) (hex - "A" + 10);
             } else {
               result = (char) (hex - "0");
             }
             return result;
           }
           private class DataBuffer {
             public char[] Buffer;
             public int Position;
             // / <summary>
             // / How much usable data has been read into the stream,
             // / which will not always be as long as Buffer.Length.
             // / </summary>
             public int Count;
             // / <summary>
             // / The position of the cursor in the buffer when the
             // / current column was started or the last time data
             // / was moved out to the column buffer.
             // / </summary>
             public int ColumnStart;
             public int LineStart;
             public DataBuffer() {
               Buffer = new char[StaticSettings.MAX_BUFFER_SIZE];
               Position = 0;
               Count = 0;
               ColumnStart = 0;
               LineStart = 0;
             }
           }
           private class ColumnBuffer {
             public char[] Buffer;
             public int Position;
             public ColumnBuffer() {
               Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE];
               Position = 0;
             }
           }
           private class RawRecordBuffer {
             public char[] Buffer;
             public int Position;
             public RawRecordBuffer() {
               Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE
                   * StaticSettings.INITIAL_COLUMN_COUNT];
               Position = 0;
             }
           }
           private class Letters {
             public static final char LF = "\n";
             public static final char CR = "\r";
             public static final char QUOTE = """;
             public static final char COMMA = ",";
             public static final char SPACE = " ";
             public static final char TAB = "\t";
             public static final char POUND = "#";
             public static final char BACKSLASH = "\\";
             public static final char NULL = "\0";
             public static final char BACKSPACE = "\b";
             public static final char FORM_FEED = "\f";
             public static final char ESCAPE = "\u001B"; // ASCII/ANSI escape
             public static final char VERTICAL_TAB = "\u000B";
             public static final char ALERT = "\u0007";
           }
           private class UserSettings {
             // having these as publicly accessible members will prevent
             // the overhead of the method call that exists on properties
             public boolean CaseSensitive;
             public char TextQualifier;
             public boolean TrimWhitespace;
             public boolean UseTextQualifier;
             public char Delimiter;
             public char RecordDelimiter;
             public char Comment;
             public boolean UseComments;
             public int EscapeMode;
             public boolean SafetySwitch;
             public boolean SkipEmptyRecords;
             public boolean CaptureRawRecord;
             public UserSettings() {
               CaseSensitive = true;
               TextQualifier = Letters.QUOTE;
               TrimWhitespace = true;
               UseTextQualifier = true;
               Delimiter = Letters.ruMA;
               RecordDelimiter = Letters.NULL;
               Comment = Letters.POUND;
               UseComments = false;
               EscapeMode = CsvReader.ESCAPE_MODE_DOUBLED;
               SafetySwitch = true;
               SkipEmptyRecords = true;
               CaptureRawRecord = true;
             }
           }
           private class HeadersHolder {
             public String[] Headers;
             public int Length;
             public HashMap<String, Integer> IndexByName;
             public HeadersHolder() {
               Headers = null;
               Length = 0;
               IndexByName = new HashMap<String, Integer>();
             }
           }
           private class StaticSettings {
             // these are static instead of final so they can be changed in unit test
             // isn"t visible outside this class and is only accessed once during
             // CsvReader construction
             public static final int MAX_BUFFER_SIZE = 1024;
             public static final int MAX_FILE_BUFFER_SIZE = 4 * 1024;
             public static final int INITIAL_COLUMN_COUNT = 10;
             public static final int INITIAL_COLUMN_BUFFER_SIZE = 50;
           }
          

          }

           </source>
             
            
           
            
          



          CSV Writer

             <source lang="java">
           
          

          /**

          Copyright 2005 Bytecode Pty Ltd.
          Licensed under the Apache License, Version 2.0 (the "License");
          you may not use this file except in compliance with the License.
          You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
          Unless required by applicable law or agreed to in writing, software
          distributed under the License is distributed on an "AS IS" BASIS,
          WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          See the License for the specific language governing permissions and
          limitations under the License.
          */
          

          import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; import java.math.BigDecimal; import java.sql.Clob; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.text.SimpleDateFormat; import java.util.Iterator; import java.util.List; /**

          * A very simple CSV writer released under a commercial-friendly license.
          *
          * @author Glen Smith
          *
          */
          

          public class CSVWriter {

             private Writer rawWriter;
             private PrintWriter pw;
             private char separator;
             private char quotechar;
             
             private char escapechar;
             
             private String lineEnd;
             /** The character used for escaping quotes. */
             public static final char DEFAULT_ESCAPE_CHARACTER = """;
             /** The default separator to use if none is supplied to the constructor. */
             public static final char DEFAULT_SEPARATOR = ",";
             /**
              * The default quote character to use if none is supplied to the
              * constructor.
              */
             public static final char DEFAULT_QUOTE_CHARACTER = """;
             
             /** The quote constant to use when you wish to suppress all quoting. */
             public static final char NO_QUOTE_CHARACTER = "\u0000";
             
             /** The escape constant to use when you wish to suppress all escaping. */
             public static final char NO_ESCAPE_CHARACTER = "\u0000";
             
             /** Default line terminator uses platform encoding. */
             public static final String DEFAULT_LINE_END = "\n";
             private static final SimpleDateFormat
               TIMESTAMP_FORMATTER = 
                 new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
             private static final SimpleDateFormat
               DATE_FORMATTER = 
                 new SimpleDateFormat("dd-MMM-yyyy");
             
             /**
              * Constructs CSVWriter using a comma for the separator.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              */
             public CSVWriter(Writer writer) {
                 this(writer, DEFAULT_SEPARATOR);
             }
             /**
              * Constructs CSVWriter with supplied separator.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries.
              */
             public CSVWriter(Writer writer, char separator) {
                 this(writer, separator, DEFAULT_QUOTE_CHARACTER);
             }
             /**
              * Constructs CSVWriter with supplied separator and quote char.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              */
             public CSVWriter(Writer writer, char separator, char quotechar) {
               this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER);
             }
             /**
              * Constructs CSVWriter with supplied separator and quote char.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              * @param escapechar
              *            the character to use for escaping quotechars or escapechars
              */
             public CSVWriter(Writer writer, char separator, char quotechar, char escapechar) {
                 this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END);
             }
             
             
             /**
              * Constructs CSVWriter with supplied separator and quote char.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              * @param lineEnd
              *        the line feed terminator to use
              */
             public CSVWriter(Writer writer, char separator, char quotechar, String lineEnd) {
                 this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd);
             }   
             
             
             
             /**
              * Constructs CSVWriter with supplied separator, quote char, escape char and line ending.
              *
              * @param writer
              *            the writer to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              * @param escapechar
              *            the character to use for escaping quotechars or escapechars
              * @param lineEnd
              *        the line feed terminator to use
              */
             public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
                 this.rawWriter = writer;
                 this.pw = new PrintWriter(writer);
                 this.separator = separator;
                 this.quotechar = quotechar;
                 this.escapechar = escapechar;
                 this.lineEnd = lineEnd;
             }
             
             /**
              * Writes the entire list to a CSV file. The list is assumed to be a
              * String[]
              *
              * @param allLines
              *            a List of String[], with each String[] representing a line of
              *            the file.
              */
             public void writeAll(List allLines)  {
                 for (Iterator iter = allLines.iterator(); iter.hasNext();) {
                     String[] nextLine = (String[]) iter.next();
                     writeNext(nextLine);
                 }
             }
             protected void writeColumnNames(ResultSetMetaData metadata)
               throws SQLException {
               
               int columnCount =  metadata.getColumnCount();
               
               String[] nextLine = new String[columnCount];
             for (int i = 0; i < columnCount; i++) {
               nextLine[i] = metadata.getColumnName(i + 1);
             }
               writeNext(nextLine);
             }
             
             /**
              * Writes the entire ResultSet to a CSV file.
              *
              * The caller is responsible for closing the ResultSet.
              *
              * @param rs the recordset to write
              * @param includeColumnNames true if you want column names in the output, false otherwise
              *
              */
             public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames)  throws SQLException, IOException {
               
               ResultSetMetaData metadata = rs.getMetaData();
               
               
               if (includeColumnNames) {
               writeColumnNames(metadata);
             }
               int columnCount =  metadata.getColumnCount();
               
               while (rs.next())
               {
                   String[] nextLine = new String[columnCount];
                   
                   for (int i = 0; i < columnCount; i++) {
                 nextLine[i] = getColumnValue(rs, metadata.getColumnType(i + 1), i + 1);
               }
                   
                 writeNext(nextLine);
               }
             }
             
             private static String getColumnValue(ResultSet rs, int colType, int colIndex)
                 throws SQLException, IOException {
               String value = "";
               
             switch (colType)
             {
               case Types.BIT:
                 Object bit = rs.getObject(colIndex);
                 if (bit != null) {
                   value = String.valueOf(bit);
                 }
               break;
               case Types.BOOLEAN:
                 boolean b = rs.getBoolean(colIndex);
                 if (!rs.wasNull()) {
                   value = Boolean.valueOf(b).toString();
                 }
               break;
               case Types.CLOB:
                 Clob c = rs.getClob(colIndex);
                 if (c != null) {
                   value = read(c);
                 }
               break;
               case Types.BIGINT:
               case Types.DECIMAL:
               case Types.DOUBLE:
               case Types.FLOAT:
               case Types.REAL:
               case Types.NUMERIC:
                 BigDecimal bd = rs.getBigDecimal(colIndex);
                 if (bd != null) {
                   value = "" + bd.doubleValue();
                 }
               break;
               case Types.INTEGER:
               case Types.TINYINT:
               case Types.SMALLINT:
                 int intValue = rs.getInt(colIndex);
                 if (!rs.wasNull()) {
                   value = "" + intValue;
                 }
               break;
               case Types.JAVA_OBJECT:
                 Object obj = rs.getObject(colIndex);
                 if (obj != null) {
                   value = String.valueOf(obj);
                 }
               break;
               case Types.DATE:
                 java.sql.Date date = rs.getDate(colIndex);
                 if (date != null) {
                   value = DATE_FORMATTER.format(date);;
                 }
               break;
               case Types.TIME:
                 Time t = rs.getTime(colIndex);
                 if (t != null) {
                   value = t.toString();
                 }
               break;
               case Types.TIMESTAMP:
                 Timestamp tstamp = rs.getTimestamp(colIndex);
                 if (tstamp != null) {
                   value = TIMESTAMP_FORMATTER.format(tstamp);
                 }
               break;
               case Types.LONGVARCHAR:
               case Types.VARCHAR:
               case Types.CHAR:
                 value = rs.getString(colIndex);
               break;
               default:
                 value = "";
             }
             
             if (value == null)
             {
               value = "";
             }
             
             return value;
               
             }
           private static String read(Clob c) throws SQLException, IOException
           {
             StringBuffer sb = new StringBuffer( (int) c.length());
             Reader r = c.getCharacterStream();
             char[] cbuf = new char[2048];
             int n = 0;
             while ((n = r.read(cbuf, 0, cbuf.length)) != -1) {
               if (n > 0) {
                 sb.append(cbuf, 0, n);
               }
             }
             return sb.toString();
           }
             
             /**
              * Writes the next line to the file.
              *
              * @param nextLine
              *            a string array with each comma-separated element as a separate
              *            entry.
              */
             public void writeNext(String[] nextLine) {
               
               if (nextLine == null)
                 return;
               
                 StringBuffer sb = new StringBuffer();
                 for (int i = 0; i < nextLine.length; i++) {
                     if (i != 0) {
                         sb.append(separator);
                     }
                     String nextElement = nextLine[i];
                     if (nextElement == null)
                         continue;
                     if (quotechar !=  NO_QUOTE_CHARACTER)
                       sb.append(quotechar);
                     for (int j = 0; j < nextElement.length(); j++) {
                         char nextChar = nextElement.charAt(j);
                         if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
                           sb.append(escapechar).append(nextChar);
                         } else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
                           sb.append(escapechar).append(nextChar);
                         } else {
                             sb.append(nextChar);
                         }
                     }
                     if (quotechar != NO_QUOTE_CHARACTER)
                       sb.append(quotechar);
                 }
                 
                 sb.append(lineEnd);
                 pw.write(sb.toString());
             }
             /**
              * Flush underlying stream to writer.
              * 
              * @throws IOException if bad things happen
              */
             public void flush() throws IOException {
                 pw.flush();
             } 
             /**
              * Close the underlying stream writer flushing any buffered content.
              *
              * @throws IOException if bad things happen
              *
              */
             public void close() throws IOException {
                 pw.flush();
                 pw.close();
                 rawWriter.close();
             }
          

          } ////////////////////////////////////////

          /**

          Copyright 2005 Bytecode Pty Ltd.
          Licensed under the Apache License, Version 2.0 (the "License");
          you may not use this file except in compliance with the License.
          You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
          Unless required by applicable law or agreed to in writing, software
          distributed under the License is distributed on an "AS IS" BASIS,
          WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          See the License for the specific language governing permissions and
          limitations under the License.
          */
          

          import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import junit.framework.TestCase; public class CSVWriterTest extends TestCase {

             /**
              * Test routine for converting output to a string.
              *
              * @param args
              *            the elements of a line of the cvs file
              * @return a String version
              * @throws IOException
              *             if there are problems writing
              */
             private String invokeWriter(String[] args) throws IOException {
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw,",","\"");
                 csvw.writeNext(args);
                 return sw.toString();
             }
             
             /**
              * Tests parsing individual lines.
              *
              * @throws IOException
              *             if the reader fails.
              */
             public void testParseLine() throws IOException {
                 // test normal case
                 String[] normal = { "a", "b", "c" };
                 String output = invokeWriter(normal);
                 assertEquals(""a","b","c"\n", output);
                 // test quoted commas
                 String[] quoted = { "a", "b,b,b", "c" };
                 output = invokeWriter(quoted);
                 assertEquals(""a","b,b,b","c"\n", output);
                 // test empty elements
                 String[] empty = { , };
                 output = invokeWriter(empty);
                 assertEquals("\n", output);
                 // test multiline quoted
                 String[] multiline = { "This is a \n multiline entry", "so is \n this" };
                 output = invokeWriter(multiline);
                 assertEquals(""This is a \n multiline entry","so is \n this"\n", output);
             }
             /**
              * Test parsing from to a list.
              *
              * @throws IOException
              *             if the reader fails.
              */
             public void testParseAll() throws IOException {
                 List allElements = new ArrayList();
                 String[] line1 = "Name#Phone#Email".split("#");
                 String[] line2 = "Glen#1234#glen@abcd.ru".split("#");
                 String[] line3 = "John#5678#john@efgh.ru".split("#");
                 allElements.add(line1);
                 allElements.add(line2);
                 allElements.add(line3);
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw);
                 csvw.writeAll(allElements);
                 String result = sw.toString();
                 String[] lines = result.split("\n");
                 assertEquals(3, lines.length);
             }
             /**
              * Tests the option of having omitting quotes in the output stream.
              * 
              * @throws IOException if bad things happen
              */
             public void testNoQuoteChars() throws IOException {
               
                 String[] line = {"Foo","Bar","Baz"};
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER);
                 csvw.writeNext(line);
                 String result = sw.toString();
                 assertEquals("Foo,Bar,Baz\n",result);
             }
             
             /**
              * Test null values.
              *
              * @throws IOException if bad things happen
              */
             public void testNullValues() throws IOException {
                 String[] line = {"Foo",null,"Bar","baz"};
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw);
                 csvw.writeNext(line);
                 String result = sw.toString();
                 assertEquals("\"Foo\",,\"Bar\",\"baz\"\n",result);
             }
             public void testStreamFlushing() throws IOException {
                 String WRITE_FILE = "myfile.csv";
                 String[] nextLine = new String[]{"aaaa", "bbbb","cccc","dddd"};
                 FileWriter fileWriter = new FileWriter(WRITE_FILE);
                 CSVWriter writer = new CSVWriter(fileWriter);
                 writer.writeNext(nextLine);
                 // If this line is not executed, it is not written in the file.
                 writer.close();
             }
             public void testAlternateEscapeChar() {
                 String[] line = {"Foo","bar"s"};
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw,CSVWriter.DEFAULT_SEPARATOR,CSVWriter.DEFAULT_QUOTE_CHARACTER,"\"");
                 csvw.writeNext(line);
                 assertEquals("\"Foo\",\"bar""s\"\n",sw.toString());
             }
             
             public void testNoQuotingNoEscaping() {
                 String[] line = {"\"Foo\",\"Bar\""};
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw,CSVWriter.DEFAULT_SEPARATOR,CSVWriter.NO_QUOTE_CHARACTER,CSVWriter.NO_ESCAPE_CHARACTER);
                 csvw.writeNext(line);
                 assertEquals("\"Foo\",\"Bar\"\n",sw.toString());
             }
             
             public void testNestedQuotes(){
                 String[] data = new String[]{"\"\"", "test"};
                 String oracle = new String("\"\"\"\"\"\",\"test\"\n");
                 CSVWriter writer=null;
                 File tempFile=null;
                 FileWriter fwriter=null;
                 try{
                     tempFile = File.createTempFile("csvWriterTest", ".csv");
                     tempFile.deleteOnExit();
                     fwriter = new FileWriter(tempFile);
                     writer = new CSVWriter(fwriter);
                 }catch(IOException e){
                     fail();
                 }
                 // write the test data:
                 writer.writeNext(data);
                 try{
                     writer.close();
                 }catch(IOException e){
                     fail();
                 }
                 try{
                     // assert that the writer was also closed.
                     fwriter.flush();
                     fail();
                 }catch(IOException e){
                     // we should go through here..
                 }
                 // read the data and compare.
                 FileReader in=null;
                 try{
                     in = new FileReader(tempFile);
                 }catch(FileNotFoundException e){
                     fail();
                 }
                 StringBuffer fileContents = new StringBuffer();
                 try{
                     int ch;
                     while((ch = in.read()) != -1){
                         fileContents.append((char)ch);
                     }
                     in.close();
                 }catch(IOException e){
                     fail();
                 }
                 assertTrue(oracle.equals(fileContents.toString()));
             }
             
             public void testAlternateLineFeeds() {
                 String[] line = {"Foo","Bar","baz"};
                 StringWriter sw = new StringWriter();
                 CSVWriter csvw = new CSVWriter(sw, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.DEFAULT_QUOTE_CHARACTER, "\r");
                 csvw.writeNext(line);
                 String result = sw.toString();
                 
                 assertTrue(result.endsWith("\r"));
               
             }
             /**
              * The Test Runner for commandline use.
              *
              * @param args
              *            no args required
              */
             public static void main(String args[]) {
                 junit.textui.TestRunner.run(CSVWriterTest.class);
             }
          

          }


           </source>
             
            
           
            
          



          CVS reader

             <source lang="java">
           
          

          package au.ru.bytecode.opencsv; /**

          Copyright 2005 Bytecode Pty Ltd.
          Licensed under the Apache License, Version 2.0 (the "License");
          you may not use this file except in compliance with the License.
          You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
          Unless required by applicable law or agreed to in writing, software
          distributed under the License is distributed on an "AS IS" BASIS,
          WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          See the License for the specific language governing permissions and
          limitations under the License.
          */
          

          import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; /**

          * A very simple CSV reader released under a commercial-friendly license.
          * 
          * @author Glen Smith
          * 
          */
          

          public class CSVReader {

             private BufferedReader br;
             private boolean hasNext = true;
             private char separator;
             private char quotechar;
             
             private int skipLines;
             private boolean linesSkiped;
             /** The default separator to use if none is supplied to the constructor. */
             public static final char DEFAULT_SEPARATOR = ",";
             /**
              * The default quote character to use if none is supplied to the
              * constructor.
              */
             public static final char DEFAULT_QUOTE_CHARACTER = """;
             
             /**
              * The default line to start reading.
              */
             public static final int DEFAULT_SKIP_LINES = 0;
             /**
              * Constructs CSVReader using a comma for the separator.
              * 
              * @param reader
              *            the reader to an underlying CSV source.
              */
             public CSVReader(Reader reader) {
                 this(reader, DEFAULT_SEPARATOR);
             }
             /**
              * Constructs CSVReader with supplied separator.
              * 
              * @param reader
              *            the reader to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries.
              */
             public CSVReader(Reader reader, char separator) {
                 this(reader, separator, DEFAULT_QUOTE_CHARACTER);
             }
             
             
             /**
              * Constructs CSVReader with supplied separator and quote char.
              * 
              * @param reader
              *            the reader to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              */
             public CSVReader(Reader reader, char separator, char quotechar) {
                 this(reader, separator, quotechar, DEFAULT_SKIP_LINES);
             }
             
             /**
              * Constructs CSVReader with supplied separator and quote char.
              * 
              * @param reader
              *            the reader to an underlying CSV source.
              * @param separator
              *            the delimiter to use for separating entries
              * @param quotechar
              *            the character to use for quoted elements
              * @param line
              *            the line number to skip for start reading 
              */
             public CSVReader(Reader reader, char separator, char quotechar, int line) {
                 this.br = new BufferedReader(reader);
                 this.separator = separator;
                 this.quotechar = quotechar;
                 this.skipLines = line;
             }
             /**
              * Reads the entire file into a List with each element being a String[] of
              * tokens.
              * 
              * @return a List of String[], with each String[] representing a line of the
              *         file.
              * 
              * @throws IOException
              *             if bad things happen during the read
              */
             public List readAll() throws IOException {
                 List allElements = new ArrayList();
                 while (hasNext) {
                     String[] nextLineAsTokens = readNext();
                     if (nextLineAsTokens != null)
                         allElements.add(nextLineAsTokens);
                 }
                 return allElements;
             }
             /**
              * Reads the next line from the buffer and converts to a string array.
              * 
              * @return a string array with each comma-separated element as a separate
              *         entry.
              * 
              * @throws IOException
              *             if bad things happen during the read
              */
             public String[] readNext() throws IOException {
                 String nextLine = getNextLine();
                 return hasNext ? parseLine(nextLine) : null;
             }
             /**
              * Reads the next line from the file.
              * 
              * @return the next line from the file without trailing newline
              * @throws IOException
              *             if bad things happen during the read
              */
             private String getNextLine() throws IOException {
               if (!this.linesSkiped) {
                     for (int i = 0; i < skipLines; i++) {
                         br.readLine();
                     }
                     this.linesSkiped = true;
                 }
                 String nextLine = br.readLine();
                 if (nextLine == null) {
                     hasNext = false;
                 }
                 return hasNext ? nextLine : null;
             }
             /**
              * Parses an incoming String and returns an array of elements.
              * 
              * @param nextLine
              *            the string to parse
              * @return the comma-tokenized list of elements, or null if nextLine is null
              * @throws IOException if bad things happen during the read
              */
             private String[] parseLine(String nextLine) throws IOException {
                 if (nextLine == null) {
                     return null;
                 }
                 List tokensOnThisLine = new ArrayList();
                 StringBuffer sb = new StringBuffer();
                 boolean inQuotes = false;
                 do {
                   if (inQuotes) {
                         // continuing a quoted section, reappend newline
                         sb.append("\n");
                         nextLine = getNextLine();
                         if (nextLine == null)
                             break;
                     }
                     for (int i = 0; i < nextLine.length(); i++) {
                         char c = nextLine.charAt(i);
                         if (c == quotechar) {
                           // this gets complex... the quote may end a quoted block, or escape another quote.
                           // do a 1-char lookahead:
                           if( inQuotes  // we are in quotes, therefore there can be escaped quotes in here.
                               && nextLine.length() > (i+1)  // there is indeed another character to check.
                               && nextLine.charAt(i+1) == quotechar ){ // ..and that char. is a quote also.
                             // we have two quote chars in a row == one quote char, so consume them both and
                             // put one on the token. we do *not* exit the quoted text.
                             sb.append(nextLine.charAt(i+1));
                             i++;
                           }else{
                             inQuotes = !inQuotes;
                             // the tricky case of an embedded quote in the middle: a,bc"d"ef,g
                             if(i>2 //not on the begining of the line
                                 && nextLine.charAt(i-1) != this.separator //not at the begining of an escape sequence 
                                 && nextLine.length()>(i+1) &&
                                 nextLine.charAt(i+1) != this.separator //not at the end of an escape sequence
                             ){
                               sb.append(c);
                             }
                           }
                         } else if (c == separator && !inQuotes) {
                             tokensOnThisLine.add(sb.toString());
                             sb = new StringBuffer(); // start work on next token
                         } else {
                             sb.append(c);
                         }
                     }
                 } while (inQuotes);
                 tokensOnThisLine.add(sb.toString());
                 return (String[]) tokensOnThisLine.toArray(new String[0]);
             }
             /**
              * Closes the underlying reader.
              * 
              * @throws IOException if the close fails
              */
             public void close() throws IOException{
               br.close();
             }
             
          

          } /////////////////////////////////////////// package au.ru.bytecode.opencsv; /**

          Copyright 2005 Bytecode Pty Ltd.
          Licensed under the Apache License, Version 2.0 (the "License");
          you may not use this file except in compliance with the License.
          You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
          Unless required by applicable law or agreed to in writing, software
          distributed under the License is distributed on an "AS IS" BASIS,
          WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          See the License for the specific language governing permissions and
          limitations under the License.
          */
          

          import java.io.IOException; import java.io.StringReader; import java.util.List; import junit.framework.TestCase; public class CSVReaderTest extends TestCase {

           CSVReader csvr;
           
           /**
            * Setup the test.
            */
           protected void setUp() throws Exception {
             StringBuffer sb = new StringBuffer();
             sb.append("a,b,c").append("\n");   // standard case
             sb.append("a,\"b,b,b\",c").append("\n");  // quoted elements
             sb.append(",,").append("\n"); // empty elements
             sb.append("a,\"PO Box 123,\nKippax,ACT. 2615.\nAustralia\",d.\n");
             sb.append("\"Glen \"\"The Man\"\" Smith\",Athlete,Developer\n"); // Test quoted quote chars
             sb.append("\"\"\"\"\"\",\"test\"\n"); // """""","test"  representing:  "", test
             sb.append("\"a\nb\",b,\"\nd\",e\n");
             csvr = new CSVReader(new StringReader(sb.toString()));
           }
          
           /**
            * Tests iterating over a reader.
            * 
            * @throws IOException
            *             if the reader fails.
            */
           public void testParseLine() throws IOException {
             // test normal case
             String[] nextLine = csvr.readNext();
             assertEquals("a", nextLine[0]);
             assertEquals("b", nextLine[1]);
             assertEquals("c", nextLine[2]);
             // test quoted commas
             nextLine = csvr.readNext();
             assertEquals("a", nextLine[0]);
             assertEquals("b,b,b", nextLine[1]);
             assertEquals("c", nextLine[2]);
             // test empty elements
             nextLine = csvr.readNext();
             assertEquals(3, nextLine.length);
             
             // test multiline quoted
             nextLine = csvr.readNext();
             assertEquals(3, nextLine.length);
             
             // test quoted quote chars
             nextLine = csvr.readNext();
             assertEquals("Glen \"The Man\" Smith", nextLine[0]);
             
             nextLine = csvr.readNext();
             assertTrue(nextLine[0].equals("\"\"")); // check the tricky situation
             assertTrue(nextLine[1].equals("test")); // make sure we didn"t ruin the next field..
             
             nextLine = csvr.readNext();
             assertEquals(4, nextLine.length);
             
             //test end of stream
             assertEquals(null, csvr.readNext());
           }
           /**
            * Test parsing to a list.
            * 
            * @throws IOException
            *             if the reader fails.
            */
           public void testParseAll() throws IOException {
             List allElements = csvr.readAll();
             assertEquals(7, allElements.size());
           }
           
           /**
            * Tests constructors with optional delimiters and optional quote char.
            * 
            * @throws IOException if the reader fails.
            */
           public void testOptionalConstructors() throws IOException {
             
             StringBuffer sb = new StringBuffer();
             sb.append("a\tb\tc").append("\n");   // tab separated case
             sb.append("a\t"b\tb\tb"\tc").append("\n");  // single quoted elements
             CSVReader c = new CSVReader(new StringReader(sb.toString()), "\t", "\"");
             
             String[] nextLine = c.readNext();
             assertEquals(3, nextLine.length);
             nextLine = c.readNext();
             assertEquals(3, nextLine.length);
             
           }
           
           /**
            * Tests option to skip the first few lines of a file.
            * 
            * @throws IOException if bad things happen
            */
           public void testSkippingLines() throws IOException {
             
             StringBuffer sb = new StringBuffer();
             sb.append("Skip this line\t with tab").append("\n");   // should skip this
             sb.append("And this line too").append("\n");   // and this
             sb.append("a\t"b\tb\tb"\tc").append("\n");  // single quoted elements
             CSVReader c = new CSVReader(new StringReader(sb.toString()), "\t", "\"", 2);
             
             String[] nextLine = c.readNext();
             assertEquals(3, nextLine.length);
             
             assertEquals("a", nextLine[0]);
           }
           
           /**
            * Tests quotes in the middle of an element.
            * 
            * @throws IOException if bad things happen
            */
           public void testParsedLineWithInternalQuota() throws IOException {
             StringBuffer sb = new StringBuffer();
             sb.append("a,123\"4\"567,c").append("\n");// a,123"4",c
             CSVReader c = new CSVReader(new StringReader(sb.toString()));
             String[] nextLine = c.readNext();
             assertEquals(3, nextLine.length);
             System.out.println(nextLine[1]);
             assertEquals("123\"4\"567", nextLine[1]);
           }
           /**
            * The Test Runner for commandline use.
            * 
            * @param args
            *            no args required
            */
           public static void main(String args[]) {
             junit.textui.TestRunner.run(CSVReaderTest.class);
           }
          

          }


           </source>
             
            
           
            
          



          Helper class to write table data to a csv-file (comma separated values).

             <source lang="java">
           
          
          

          /* Copyright (c) 2001-2009, The HSQL Development Group

          * All rights reserved.
          *
          * Redistribution and use in source and binary forms, with or without
          * modification, are permitted provided that the following conditions are met:
          *
          * Redistributions of source code must retain the above copyright notice, this
          * list of conditions and the following disclaimer.
          *
          * Redistributions in binary form must reproduce the above copyright notice,
          * this list of conditions and the following disclaimer in the documentation
          * and/or other materials provided with the distribution.
          *
          * Neither the name of the HSQL Development Group nor the names of its
          * contributors may be used to endorse or promote products derived from this
          * software without specific prior written permission.
          *
          * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
          * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
          * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
          * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
          * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
          * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
          * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
          * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
          * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
          * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
          * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
          */
          

          import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; /**

          * helper class to write table data to a csv-file (comma separated values).
          * the first line in file is a list of fieldnames, all following lines
          * are data lines.
          * a descptiontion of file format can be found on: http://www.wotsit.org/
          * usage: create a object using the constructor. call writeHeader
          * for writing the filename header then add data with writeData.
          * at the end close() closes the file.
          *
          *@author jeberle@users
          */
          

          public class CSVWriter {

             private String             newline = System.getProperty("line.separator");
             private OutputStreamWriter writer  = null;
             private int                nbrCols = 0;
             private int                nbrRows = 0;
             /**
              * constructor.
              * creates a csv file for writing data to it
              * @param file the file to write data to
              * @param encoding encoding to use or null (=defualt)
              */
             public CSVWriter(File file, String encoding) throws IOException {
                 if (encoding == null) {
                     encoding = System.getProperty("file.encoding");
                 }
                 FileOutputStream fout = new FileOutputStream(file);
                 writer = new OutputStreamWriter(fout, encoding);
             }
             /**
              * writes the csv header (fieldnames). should be called after
              * construction one time.
              * @param header String[] with fieldnames
              */
             public void writeHeader(String[] header) throws IOException {
                 this.nbrCols = header.length;
                 doWriteData(header);
             }
             /**
              * writes a data-record to the file. note that data[] must have
              * same number of elements as the header had.
              *
              * @param data data to write to csv-file
              */
             public void writeData(String[] data) throws IOException {
                 doWriteData(data);
             }
             /**
              * closes the csv file.
              */
             public void close() throws IOException {
                 this.writer.close();
             }
             private void doWriteData(String[] values) throws IOException {
                 for (int i = 0; i < values.length; i++) {
                     if (i > 0) {
                         this.writer.write(";");
                     }
                     if (values[i] != null) {
                         this.writer.write("\"");
                         this.writer.write(this.toCsvValue(values[i]));
                         this.writer.write("\"");
                     }
                 }
                 this.writer.write(newline);
                 this.nbrRows++;
             }
             private String toCsvValue(String str) {
                 StringBuffer sb = new StringBuffer();
                 for (int i = 0; i < str.length(); i++) {
                     char c = str.charAt(i);
                     sb.append(c);
                     switch (c) {
                         case """ :
                             sb.append(""");
                             break;
                     }
                 }
                 return sb.toString();
             }
          

          }


           </source>
             
            
           
            
          



          Reads CSV (Comma Separated Value) files

             <source lang="java">
           
          

          /*------------------------------------------------------------------------------ Name: CSVReader.java Project: jutils.org Comment: Reads CSV (Comma Separated Value) files Version: $Id: CSVReader.java,v 1.1 2004/04/07 07:40:45 laurent Exp $ Author: Roedy Green roedy@mindprod.ru, Heinrich Goetzger goetzger@gmx.net


          */

          import java.util.Vector; import java.io.BufferedReader; import java.io.EOFException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; /**

          * Reads CSV (Comma Separated Value) files.
          *
          * This format is mostly used my Microsoft Word and Excel.
          * Fields are separated by commas, and enclosed in
          * quotes if they contain commas or quotes.
          * Embedded quotes are doubled.
          * Embedded spaces do not normally require surrounding quotes.
          * The last field on the line is not followed by a comma.
          * Null fields are represented by two commas in a row.
          * We ignore leading and trailing spaces on fields, even inside quotes.
          *
          * @author copyright (c) 2002 Roedy Green  Canadian Mind Products
          * Roedy posted this code on Newsgroups:comp.lang.java.programmer on 27th March 2002.
          *
          * Heinrich added some stuff like comment ability and linewise working.
          *
          */
          

          public class CSVReader {

            /**
             * Constructor
             *
             * @param r     input Reader source of CSV Fields to read.
             * @param separator
             *               field separator character, usually "," in North America,
             *               ";" in Europe and sometimes "\t" for tab.
             */
            public CSVReader (Reader r, char separator) {
               /* convert Reader to BufferedReader if necessary */
               if ( r instanceof BufferedReader ) {
                  this.r = (BufferedReader) r;
               } else {
                  this.r = new BufferedReader(r);
               }
               this.separator = separator;
            } // end of CSVReader
            /**
             * Constructor with default field separator ",".
             *
             * @param r     input Reader source of CSV Fields to read.
             */
            public CSVReader (Reader r) {
               /* convert Reader to BufferedReader if necessary */
               if ( r instanceof BufferedReader ) {
                  this.r = (BufferedReader) r;
               } else {
                  this.r = new BufferedReader(r);
               }
               this.separator = ",";
            } // end of CSVReader
            private static final boolean debugging = true;
            /**
             * Reader source of the CSV fields to be read.
             */
            private BufferedReader r;
            /*
            * field separator character, usually "," in North America,
            * ";" in Europe and sometimes "\t" for tab.
            */
            private char separator;
            /**
             * category of end of line char.
             */
            private static final int EOL = 0;
            /**
             * category of ordinary character
             */
            private static final int ORDINARY = 1;
            /**
             * categotory of the quote mark "
             */
            private static final int QUOTE = 2;
            /**
             * category of the separator, e.g. comma, semicolon
             * or tab.
             */
            private static final int SEPARATOR = 3;
            /**
             * category of characters treated as white space.
             */
            private static final int WHITESPACE = 4;
            /**
             * categorise a character for the finite state machine.
             *
             * @param c      the character to categorise
             * @return integer representing the character"s category.
             */
            private int categorise ( char c ) {
               switch ( c ) {
                  case " ":
                  case "\r":
                  case 0xff:
                     return WHITESPACE;
          

          // case ";": // case "!":

                  case "#":
                     //return EOL;
                  case "\n":
                     return EOL; /* artificially applied to end of line */
                  case "\"":
                     return QUOTE;
                  default:
                     if (c == separator) {
                        /* dynamically determined so can"t use as case label */
                        return SEPARATOR;
                     } else if ( "!" <= c && c <= "~" ) {
                        /* do our tests in crafted order, hoping for an early return */
                        return ORDINARY;
                     } else if ( 0x00 <= c && c <= 0x20 ) {
                        return WHITESPACE;
                     } else if ( Character.isWhitespace(c) ) {
                        return WHITESPACE;
                     } else {
                        return ORDINARY;
                     }
               } // end of switch
            } // end of categorise
          
            /**
             * parser: We are in blanks before the field.
             */
            private static final int SEEKINGSTART = 0;
            /**
             * parser: We are in the middle of an ordinary field.
             */
            private static final int INPLAIN = 1;
            /**
             * parser: e are in middle of field surrounded in quotes.
             */
            private static final int INQUOTED = 2;
            /**
             * parser: We have just hit a quote, might be doubled
             * or might be last one.
             */
            private static final int AFTERENDQUOTE = 3;
            /**
            * parser: We are in blanks after the field looking for the separator
            */
            private static final int SKIPPINGTAIL = 4;
            /**
             * state of the parser"s finite state automaton.
             */
            /**
             * The line we are parsing.
             * null means none read yet.
             * Line contains unprocessed chars. Processed ones are removed.
             */
            private String line = null;
            /**
             * How many lines we have read so far.
             * Used in error messages.
             */
            private int lineCount = 0;
            public String[] getLine() {
               Vector lineArray = new Vector();
               String token = null;
               String returnArray [] = null;
               // reading values from line until null comes
               try {
                  while (lineArray.size() == 0) {
                     while ( (token = get() ) != null ) {
                        lineArray.add(token);
                     } // end of while
                  } // end of while
               } catch (EOFException e) {
                  return null;
               } catch (IOException e) {
               }
               returnArray = new String[lineArray.size()];
               for(int ii=0; ii < lineArray.size(); ii++) {
                  returnArray[ii] = lineArray.elementAt(ii).toString();
               } // end of for
               return returnArray;
            }
            /**
             * Read one field from the CSV file
             *
             * @return String value, even if the field is numeric.  Surrounded
             *         and embedded double quotes are stripped.
             *         possibly "".  null means end of line.
             *
             * @exception EOFException
             *                   at end of file after all the fields have
             *                   been read.
             *
             * @exception IOException
             *                   Some problem reading the file, possibly malformed data.
             */
            private String get() throws EOFException, IOException {
               StringBuffer field = new StringBuffer(50);
               /* we implement the parser as a finite state automaton with five states. */
               readLine();
               int state = SEEKINGSTART; /* start seeking, even if partway through a line */
               /* don"t need to maintain state between fields. */
               /* loop for each char in the line to find a field */
               /* guaranteed to leave early by hitting EOL */
               for ( int i=0; i<line.length(); i++ ) {
                  char c = line.charAt(i);
                  int category = categorise(c);
                  switch ( state ) {
                     case SEEKINGSTART: {
                        /* in blanks before field */
                        switch ( category ) {
                           case WHITESPACE:
                              /* ignore */
                              break;
                           case QUOTE:
                              state = INQUOTED;
                              break;
                           case SEPARATOR:
                              /* end of empty field */
                              line = line.substring(i+1);
                              return "";
                           case EOL:
                              /* end of line */
                              line = null;
                              return null;
                           case ORDINARY:
                              field.append(c);
                              state = INPLAIN;
                              break;
                        }
                        break;
                     } // end of SEEKINGSTART
                     case INPLAIN: {
                        /* in middle of ordinary field */
                        switch ( category ) {
                           case QUOTE:
                              throw new IOException("Malformed CSV stream. Missing quote at start of field on line " + lineCount);
                           case SEPARATOR:
                              /* done */
                              line = line.substring(i+1);
                              return field.toString().trim();
                           case EOL:
                              line = line.substring(i); /* push EOL back */
                              return field.toString().trim();
                           case WHITESPACE:
                              field.append(" ");
                              break;
                           case ORDINARY:
                              field.append(c);
                              break;
                        }
                        break;
                     } // end of INPLAIN
                     case INQUOTED: {
                        /* in middle of field surrounded in quotes */
                        switch ( category ) {
                           case QUOTE:
                              state = AFTERENDQUOTE;
                              break;
                           case EOL:
                              throw new IOException ("Malformed CSV stream. Missing quote after field on line "+lineCount);
                           case WHITESPACE:
                              field.append(" ");
                              break;
                           case SEPARATOR:
                           case ORDINARY:
                              field.append(c);
                              break;
                        }
                         break;
                     } // end of INQUOTED
                     case AFTERENDQUOTE: {
                        /* In situation like this "xxx" which may
                           turn out to be xxx""xxx" or "xxx",
                           We find out here. */
                        switch ( category ) {
                              case QUOTE:
                                 field.append(c);
                                 state = INQUOTED;
                                 break;
                              case SEPARATOR :
                                 /* we are done.*/
                                 line = line.substring(i+1);
                                 return field.toString().trim();
                              case EOL:
                                 line = line.substring(i); /* push back eol */
                                 return field.toString().trim();
                              case WHITESPACE:
                                 /* ignore trailing spaces up to separator */
                                 state = SKIPPINGTAIL;
                                 break;
                              case ORDINARY:
                                 throw new IOException("Malformed CSV stream, missing separator after field on line " + lineCount);
                        }
                        break;
                     } // end of AFTERENDQUOTE
                     case SKIPPINGTAIL: {
                        /* in spaces after field seeking separator */
                        switch ( category ) {
                           case SEPARATOR :
                              /* we are done.*/
                              line = line.substring(i+1);
                              return field.toString().trim();
                           case EOL:
                              line = line.substring(i); /* push back eol */
                              return field.toString().trim();
                           case WHITESPACE:
                              /* ignore trailing spaces up to separator */
                              break;
                           case QUOTE:
                           case ORDINARY:
                              throw new IOException("Malformed CSV stream, missing separator after field on line " + lineCount);
                        } // end of switch
                        break;
                     } // end of SKIPPINGTAIL
                  } // end switch(state)
               } // end for
               throw new IOException("Program logic bug. Should not reach here. Processing line " + lineCount);
            } // end get
            /**
             * Make sure a line is available for parsing.
             * Does nothing if there already is one.
             *
             * @exception EOFException
             */
            private void readLine() throws EOFException, IOException {
               if ( line == null ) {
                  line = r.readLine();  /* this strips platform specific line ending */
                  if ( line == null ) {
                         /* null means EOF, yet another inconsistent Java convention. */
                     throw new EOFException();
                  } else {
                     line += "\n"; /* apply standard line end for parser to find */
                     lineCount++;
                  }
               }
            } // end of readLine
          
            /**
             * Skip over fields you don"t want to process.
             *
             * @param fields How many field you want to bypass reading.
             *               The newline counts as one field.
             * @exception EOFException
             *                   at end of file after all the fields have
             *                   been read.
             * @exception IOException
             *                   Some problem reading the file, possibly malformed data.
             */
            public void skip(int fields) throws EOFException, IOException {
               if ( fields <= 0 ) {
                  return;
               }
               for ( int i=0; i<fields; i++ ) {
                  // throw results away
                  get();
               }
            } // end of skip
            /**
             * Skip over remaining fields on this line you don"t want to process.
             *
             * @exception EOFException
             *                   at end of file after all the fields have
             *                   been read.
             * @exception IOException
             *                   Some problem reading the file, possibly malformed data.
             */
            public void skipToNextLine() throws EOFException, IOException {
               if ( line == null ) {
                  readLine();
               }
               line = null;
            } // end of skipToNextLine
            /**
             * Close the Reader.
             */
            public void close() throws IOException {
               if ( r != null ) {
                  r.close();
                  r = null;
               }
            } // end of close
            /**
             * @param args  [0]: The name of the file.
             */
            private static void testSingleTokens(String[] args) {
               if ( debugging ) {
                  try {
                     // read test file
                       CSVReader csv = new CSVReader(new FileReader(args[0]), ",");
                    try {
                        while ( true ) {
                           System.out.println(csv.get());
                        }
                     } catch ( EOFException  e ) {
                         }
                         csv.close();
                  } catch ( IOException  e ) {
                     e.printStackTrace();
                     System.out.println(e.getMessage());
                  }
               } // end if
            } // end of testSingleTokens
            /**
             * @param args  [0]: The name of the file.
             */
            private static void testLines(String[] args) {
               int lineCounter = 0;
               String loadLine[] = null;
               String DEL = ",";
               if ( debugging ) {
                  try {
                     // read test file
                     CSVReader csv = new CSVReader(new FileReader(args[0]), ",");
                     while( (loadLine = csv.getLine()) != null) {
                        lineCounter++;
                        StringBuffer logBuffer = new StringBuffer();
                        String logLine;
                        //log.debug("#" + lineCounter +" : "" + loadLine.length + """);
                        logBuffer.append(loadLine[0]); // write first token, then write DEL in loop and the whole rest.
                        for(int i=1; i < loadLine.length; i++) {
                           logBuffer.append(DEL).append(loadLine[i]);
                        }
                        logLine = logBuffer.toString();
                        logLine.substring(0, logLine.lastIndexOf(DEL));
                        //logLine.delete(logLine.lastIndexOf(DEL), logLine.length()); // is supported since JDK 1.4
                        //System.out.println("#" + lineCounter +" : "" + loadLine.length + "" " + logLine);
                        System.out.println(logLine);
                     } // end of while
                         csv.close();
                  } catch ( IOException  e ) {
                     e.printStackTrace();
                     System.out.println(e.getMessage());
                  }
               } // end if
            } // end of testLines
            /**
             * Test driver
             *
             * @param args  [0]: The name of the file.
             */
            static public void main(String[] args) {
               //testSingleTokens(args);
               testLines(args);
            } // end main
          

          } // end CSVReader // end of file


           </source>
             
            
           
            
          



          Simple demo of CSV matching using Regular Expressions

             <source lang="java">
           
          

          /*

          * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
          * All rights reserved. Software written by Ian F. Darwin and others.
          * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
          *
          * Redistribution and use in source and binary forms, with or without
          * modification, are permitted provided that the following conditions
          * are met:
          * 1. Redistributions of source code must retain the above copyright
          *    notice, this list of conditions and the following disclaimer.
          * 2. Redistributions in binary form must reproduce the above copyright
          *    notice, this list of conditions and the following disclaimer in the
          *    documentation and/or other materials provided with the distribution.
          *
          * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
          * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
          * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
          * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
          * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
          * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
          * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
          * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
          * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
          * POSSIBILITY OF SUCH DAMAGE.
          * 
          * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
          * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
          * pioneering role in inventing and promulgating (and standardizing) the Java 
          * language and environment is gratefully acknowledged.
          * 
          * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
          * inventing predecessor languages C and C++ is also gratefully acknowledged.
          */
          

          import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /* Simple demo of CSV matching using Regular Expressions.

          * Does NOT use the "CSV" class defined in the Java CookBook, but uses
          * a regex pattern simplified from Chapter 7 of Mastering Regular 
          * Expressions (p. 205, first edn.)
          * @version $Id: CSVRE.java,v 1.16 2004/04/25 19:43:32 ian Exp $
          */
          

          public class CSVRE {

           /** The rather involved pattern used to match CSV"s consists of three
            * alternations: the first matches aquoted field, the second unquoted,
            * the third a null field.
            */
           public static final String CSV_PATTERN = "\"([^\"]+?)\",?|([^,]+),?|,";
           private static Pattern csvRE;
           public static void main(String[] argv) throws IOException {
             System.out.println(CSV_PATTERN);
             new CSVRE().process(new BufferedReader(new InputStreamReader(System.in)));
           }
           
           /** Construct a regex-based CSV parser. */
           public CSVRE() {
             csvRE = Pattern.rupile(CSV_PATTERN);
           }
           
           /** Process one file. Delegates to parse() a line at a time */
           public void process(BufferedReader in) throws IOException {
             String line;
             // For each line...
             while ((line = in.readLine()) != null) {
               System.out.println("line = `" + line + """);
               List l = parse(line);
               System.out.println("Found " + l.size() + " items.");
               for (int i = 0; i < l.size(); i++) {
                 System.out.print(l.get(i) + ",");
               }
               System.out.println();
             }
           }
           
           /** Parse one line.
            * @return List of Strings, minus their double quotes
            */
           public List parse(String line) {
             List list = new ArrayList();
             Matcher m = csvRE.matcher(line);
             // For each field
             while (m.find()) {
               String match = m.group();
               if (match == null)
                 break;
               if (match.endsWith(",")) {  // trim trailing ,
                 match = match.substring(0, match.length() - 1);
               }
               if (match.startsWith("\"")) { // assume also ends with
                 match = match.substring(1, match.length() - 1);
               }
               if (match.length() == 0)
                 match = null;
               list.add(match);
             }
             return list;
           }
          

          }


           </source>
             
            
           
            
          



          Simple demo of CSV parser class

             <source lang="java">
           
          

          /*

          * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
          * All rights reserved. Software written by Ian F. Darwin and others.
          * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
          *
          * Redistribution and use in source and binary forms, with or without
          * modification, are permitted provided that the following conditions
          * are met:
          * 1. Redistributions of source code must retain the above copyright
          *    notice, this list of conditions and the following disclaimer.
          * 2. Redistributions in binary form must reproduce the above copyright
          *    notice, this list of conditions and the following disclaimer in the
          *    documentation and/or other materials provided with the distribution.
          *
          * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
          * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
          * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
          * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
          * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
          * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
          * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
          * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
          * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
          * POSSIBILITY OF SUCH DAMAGE.
          * 
          * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
          * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
          * pioneering role in inventing and promulgating (and standardizing) the Java 
          * language and environment is gratefully acknowledged.
          * 
          * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
          * inventing predecessor languages C and C++ is also gratefully acknowledged.
          */
          

          import java.util.*; /* Simple demo of CSV parser class.

          */
          

          public class CSVSimple {

           public static void main(String[] args) {
             CSV parser = new CSV();
             List list = parser.parse(
               "\"LU\",86.25,\"11/4/1998\",\"2:19PM\",+4.0625");
             Iterator it = list.iterator();
             while (it.hasNext()) {
               System.out.println(it.next());
             }
             // Now test with a non-default separator
             parser = new CSV("|");
             list = parser.parse(
               "\"LU\"|86.25|\"11/4/1998\"|\"2:19PM\"|+4.0625");
             it = list.iterator();
             while (it.hasNext()) {
               System.out.println(it.next());
             }
           }
          

          } /** Parse comma-separated values (CSV), a common Windows file format.

          * Sample input: "LU",86.25,"11/4/1998","2:19PM",+4.0625
          * <p>
          * Inner logic adapted from a C++ original that was
          * Copyright (C) 1999 Lucent Technologies
          * Excerpted from "The Practice of Programming"
          * by Brian W. Kernighan and Rob Pike.
          * <p>
          * Included by permission of the http://tpop.awl.ru/ web site, 
          * which says:
          * "You may use this code for any purpose, as long as you leave 
          * the copyright notice and book citation attached." I have done so.
          * @author Brian W. Kernighan and Rob Pike (C++ original)
          * @author Ian F. Darwin (translation into Java and removal of I/O)
          * @author Ben Ballard (rewrote advQuoted to handle """" and for readability)
          */
          

          class CSV {

           public static final char DEFAULT_SEP = ",";
           /** Construct a CSV parser, with the default separator (`,"). */
           public CSV() {
             this(DEFAULT_SEP);
           }
           /** Construct a CSV parser with a given separator. 
            * @param sep The single char for the separator (not a list of
            * separator characters)
            */
           public CSV(char sep) {
             fieldSep = sep;
           }
           /** The fields in the current String */
           protected List list = new ArrayList();
           /** the separator char for this parser */
           protected char fieldSep;
           /** parse: break the input String into fields
            * @return java.util.Iterator containing each field 
            * from the original as a String, in order.
            */
           public List parse(String line)
           {
             StringBuffer sb = new StringBuffer();
             list.clear();      // recycle to initial state
             int i = 0;
             if (line.length() == 0) {
               list.add(line);
               return list;
             }
             do {
                     sb.setLength(0);
                     if (i < line.length() && line.charAt(i) == """)
                         i = advQuoted(line, sb, ++i);  // skip quote
                     else
                         i = advPlain(line, sb, i);
                     list.add(sb.toString());
               i++;
             } while (i < line.length());
             return list;
           }
           /** advQuoted: quoted field; return index of next separator */
           protected int advQuoted(String s, StringBuffer sb, int i)
           {
             int j;
             int len= s.length();
                 for (j=i; j<len; j++) {
                     if (s.charAt(j) == """ && j+1 < len) {
                         if (s.charAt(j+1) == """) {
                             j++; // skip escape char
                         } else if (s.charAt(j+1) == fieldSep) { //next delimeter
                             j++; // skip end quotes
                             break;
                         }
                     } else if (s.charAt(j) == """ && j+1 == len) { // end quotes at end of line
                         break; //done
               }
               sb.append(s.charAt(j));  // regular character.
             }
             return j;
           }
           /** advPlain: unquoted field; return index of next separator */
           protected int advPlain(String s, StringBuffer sb, int i)
           {
             int j;
             j = s.indexOf(fieldSep, i); // look for separator
                 if (j == -1) {                 // none found
                     sb.append(s.substring(i));
                     return s.length();
                 } else {
                     sb.append(s.substring(i, j));
                     return j;
                 }
             }
          

          }



           </source>
             
            
           
            
          



          The CSVQuoter is a helper class to encode a string for the CSV file format.

             <source lang="java">
           
          

          /**

          * 
          * JFreeReport : a free Java reporting library
          * 
          *
          * Project Info:  http://reporting.pentaho.org/
          *
          * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
          *
          * This library is free software; you can redistribute it and/or modify it under the terms
          * of the GNU Lesser General Public License as published by the Free Software Foundation;
          * either version 2.1 of the License, or (at your option) any later version.
          *
          * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
          * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
          * See the GNU Lesser General Public License for more details.
          *
          * You should have received a copy of the GNU Lesser General Public License along with this
          * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
          * Boston, MA 02111-1307, USA.
          *
          * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
          * in the United States and other countries.]
          *
          * ------------
          * CSVQuoter.java
          * ------------
          * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
          */
          

          /**

          * The CSVQuoter is a helper class to encode a string for the CSV file
          * format.
          *
          * @author Thomas Morgner.
          */
          

          public class CSVQuoter {

           /**
            * The separator used in the CSV file.
            */
           private char separator;
           /**
            * The quoting character or a single quote.
            */
           private char quate;
           /**
            * The double quote. This is a string containing the quate two times.
            */
           private String doubleQuate;
           /**
            * Creates a new CSVQuoter, which uses a comma as the default separator.
            */
           public CSVQuoter ()
           {
             this(",", """);
           }
           /**
            * Creates a new CSVQuoter, which uses the defined separator.
            *
            * @param separator the separator.
            * @throws NullPointerException if the given separator is null.
            */
           public CSVQuoter (final char separator)
           {
             this(separator,  """);
           }
           /**
            * Creates a new CSVQuoter with the given separator and quoting character.
            *
            * @param separator the separator
            * @param quate the quoting character
            */
           public CSVQuoter (final char separator, final char quate)
           {
             this.separator = separator;
             this.quate = quate;
             this.doubleQuate = String.valueOf(quate) + quate;
           }
           /**
            * Encodes the string, so that the string can safely be used in CSV files. If the string
            * does not need quoting, the original string is returned unchanged.
            *
            * @param original the unquoted string.
            * @return The quoted string
            */
           public String doQuoting (final String original)
           {
             if (isQuotingNeeded(original))
             {
               final StringBuffer retval = new StringBuffer();
               retval.append(quate);
               applyQuote(retval, original);
               retval.append(quate);
               return retval.toString();
             }
             else
             {
               return original;
             }
           }
           /**
            * Decodes the string, so that all escape sequences get removed. If the string was not
            * quoted, then the string is returned unchanged.
            *
            * @param nativeString the quoted string.
            * @return The unquoted string.
            */
           public String undoQuoting (final String nativeString)
           {
             if (isQuotingNeeded(nativeString))
             {
               final StringBuffer b = new StringBuffer(nativeString.length());
               final int length = nativeString.length() - 1;
               int start = 1;
               int pos = start;
               while (pos != -1)
               {
                 pos = nativeString.indexOf(doubleQuate, start);
                 if (pos == -1)
                 {
                   b.append(nativeString.substring(start, length));
                 }
                 else
                 {
                   b.append(nativeString.substring(start, pos));
                   start = pos + 1;
                 }
               }
               return b.toString();
             }
             else
             {
               return nativeString;
             }
           }
           /**
            * Tests, whether this string needs to be quoted. A string is encoded if the string
            * contains a newline character, a quote character or the defined separator.
            *
            * @param str the string that should be tested.
            * @return true, if quoting needs to be applied, false otherwise.
            */
           private boolean isQuotingNeeded (final String str)
           {
             if (str.indexOf(separator) != -1)
             {
               return true;
             }
             if (str.indexOf("\n") != -1)
             {
               return true;
             }
             if (str.indexOf(quate, 1) != -1)
             {
               return true;
             }
             return false;
           }
           /**
            * Applies the quoting to a given string, and stores the result in the StringBuffer
            * b.
            *
            * @param b        the result buffer
            * @param original the string, that should be quoted.
            */
           private void applyQuote (final StringBuffer b, final String original)
           {
             // This solution needs improvements. Copy blocks instead of single
             // characters.
             final int length = original.length();
             for (int i = 0; i < length; i++)
             {
               final char c = original.charAt(i);
               if (c == quate)
               {
                 b.append(doubleQuate);
               }
               else
               {
                 b.append(c);
               }
             }
           }
           /**
            * Gets the separator used in this quoter and the CSV file.
            *
            * @return the separator (never null).
            */
           public char getSeparator ()
           {
             return separator;
           }
           /**
            * Returns the quoting character.
            *
            * @return the quote character.
            */
           public char getQuate ()
           {
             return quate;
           }
          

          }


           </source>
             
            
           
            
          



          The csv tokenizer class allows an application to break a Comma Separated Value format into tokens.

             <source lang="java">
           
          

          /**

          * 
          * JFreeReport : a free Java reporting library
          * 
          *
          * Project Info:  http://reporting.pentaho.org/
          *
          * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
          *
          * This library is free software; you can redistribute it and/or modify it under the terms
          * of the GNU Lesser General Public License as published by the Free Software Foundation;
          * either version 2.1 of the License, or (at your option) any later version.
          *
          * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
          * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
          * See the GNU Lesser General Public License for more details.
          *
          * You should have received a copy of the GNU Lesser General Public License along with this
          * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
          * Boston, MA 02111-1307, USA.
          *
          * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
          * in the United States and other countries.]
          *
          * ------------
          * CSVTokenizer.java
          * ------------
          * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
          */
          

          import java.util.Enumeration; import java.util.NoSuchElementException; /**

          * The csv tokenizer class allows an application to break a Comma Separated Value format into tokens. The tokenization
          * method is much simpler than the one used by the StringTokenizer class. The CSVTokenizer
          * methods do not distinguish among identifiers, numbers, and quoted strings, nor do they recognize and skip comments.
          * <p/>
          * The set of separator (the characters that separate tokens) may be specified either at creation time or on a per-token
          * basis.
          * <p/>
          * An instance of CSVTokenizer behaves in one of two ways, depending on whether it was created with the
          
          * returnSeparators flag having the value true or false:
          • If the flag is * false, delimiter characters serve to separate tokens. A token is a maximal sequence of consecutive * characters that are not separator.
          • If the flag is true, delimiter characters are themselves * considered to be tokens. A token is thus either one delimiter character, or a maximal sequence of consecutive * characters that are not separator.
          <p> A CSVTokenizer object internally maintains a current position
          * within the string to be tokenized. Some operations advance this current position past the characters processed.<p> A
          * token is returned by taking a substring of the string that was used to create the CSVTokenizer object.
          * <p/>
          * The following is one example of the use of the tokenizer. The code:
          
          *
           *     CSVTokenizer csvt = new CSVTokenizer("this,is,a,test");
           *     while (csvt.hasMoreTokens()) {
           *         println(csvt.nextToken());
           *     }
           * 
          * <p/>
          * prints the following output:
          
          *
           *     this
           *     is
           *     a
           *     test
           * 
          *
          * @author abupon
          */
          

          public class CSVTokenizer implements Enumeration {

           /**
            * The complete record that should be separated into elements.
            */
           private String record;
           /**
            * The separator.
            */
           private String separator;
           /**
            * The quoting char.
            */
           private String quate;
           /**
            * the current parsing position.
            */
           private int currentIndex;
           /**
            * A flag indicating that the current parse position is before the start.
            */
           private boolean beforeStart;
           /**
            * A possible separator constant.
            */
           public static final String SEPARATOR_COMMA = ",";
           /**
            * A possible separator constant.
            */
           public static final String SEPARATOR_TAB = "\t";
           /**
            * A possible separator constant.
            */
           public static final String SEPARATOR_SPACE = " ";
           /**
            * A possible quote character constant.
            */
           public static final String DOUBLE_QUATE = "\"";
           /**
            * A possible quote character constant.
            */
           public static final String SINGLE_QUATE = """;
           /**
            * Constructs a csv tokenizer for the specified string. theSeparator argument is the separator for
            * separating tokens.
            * <p/>
            * If the returnSeparators flag is true, then the separator string is also returned as
            * tokens. separator is returned as a string. If the flag is false, the separator string is skipped and
            * only serve as separator between tokens.
            *
            * @param aString      a string to be parsed.
            * @param theSeparator the separator (CSVTokenizer.SEPARATOR_COMMA, CSVTokenizer.TAB, CSVTokenizer.SPACE, etc.).
            * @param theQuate     the quate (CSVTokenizer.SINGLE_QUATE, CSVTokenizer.DOUBLE_QUATE, etc.).
            */
           public CSVTokenizer(final String aString, final String theSeparator,
                               final String theQuate)
           {
             if (aString == null)
             {
               throw new NullPointerException("The given string is null");
             }
             if (theSeparator == null)
             {
               throw new NullPointerException("The given separator is null");
             }
             if (theQuate == null)
             {
               throw new NullPointerException("The given quate is null");
             }
             this.record = aString.trim();
             this.separator = theSeparator;
             this.quate = theQuate;
             this.currentIndex = 0;
             this.beforeStart = true;
           }
           /**
            * Constructs a csv tokenizer for the specified string. The characters in the theSeparator argument are
            * the separator for separating tokens. Separator string themselves will not be treated as tokens.
            *
            * @param aString      a string to be parsed.
            * @param theSeparator the separator (CSVTokenizer.SEPARATOR_COMMA, CSVTokenizer.TAB, CSVTokenizer.SPACE, etc.).
            */
           public CSVTokenizer(final String aString, final String theSeparator)
           {
             this(aString, theSeparator, CSVTokenizer.DOUBLE_QUATE);
           }
           /**
            * Constructs a string tokenizer for the specified string. The tokenizer uses the default separator set, which is
            * CSVTokenizer.SEPARATOR_COMMA. Separator string themselves will not be treated as tokens.
            *
            * @param aString a string to be parsed.
            */
           public CSVTokenizer(final String aString)
           {
             this(aString, CSVTokenizer.SEPARATOR_COMMA);
           }
           /**
            * Tests if there are more tokens available from this tokenizer"s string. If this method returns true, then a
            * subsequent call to nextToken with no argument will successfully return a token.
            *
            * @return true if and only if there is at least one token in the string after the current position;
            *         false otherwise.
            */
           public boolean hasMoreTokens()
           {
             return (this.currentIndex < this.record.length());
           }
           /**
            * Returns the next token from this string tokenizer.
            *
            * @return the next token from this string tokenizer.
            * @throws NoSuchElementException   if there are no more tokens in this tokenizer"s string.
            * @throws IllegalArgumentException if given parameter string format was wrong
            */
           public String nextToken()
               throws NoSuchElementException, IllegalArgumentException
           {
             if (!this.hasMoreTokens())
             {
               throw new NoSuchElementException();
             }
             if (beforeStart == false)
             {
               currentIndex += this.separator.length();
             }
             else
             {
               beforeStart = false;
             }
             if (this.record.startsWith(this.quate, this.currentIndex))
             {
               final StringBuffer token = new StringBuffer();
               String rec = this.record.substring(this.currentIndex + this.quate.length());
               while (true)
               {
                 final int end = rec.indexOf(this.quate);
                 if (end < 0)
                 {
                   throw new IllegalArgumentException("Illegal format");
                 }
                 if (!rec.startsWith(this.quate, end + 1))
                 {
                   token.append(rec.substring(0, end));
                   break;
                 }
                 token.append(rec.substring(0, end + 1));
                 rec = rec.substring(end + this.quate.length() * 2);
                 this.currentIndex++;
               }
               this.currentIndex += (token.length() + this.quate.length() * 2);
               return token.toString();
             }
             final int end = this.record.indexOf(this.separator, this.currentIndex);
             if (end >= 0)
             {
               final int start = this.currentIndex;
               final String token = this.record.substring(start, end);
               this.currentIndex = end;
               return token;
             }
             else
             {
               final int start = this.currentIndex;
               final String token = this.record.substring(start);
               this.currentIndex = this.record.length();
               return token;
             }
           }
           /**
            * Returns the next token in this string tokenizer"s string. First, the set of characters considered to be separator
            * by this CSVTokenizer object is changed to be the characters in the string separator. Then the
            * next token in the string after the current position is returned. The current position is advanced beyond the
            * recognized token.  The new delimiter set remains the default after this call.
            *
            * @param theSeparator the new separator.
            * @return the next token, after switching to the new delimiter set.
            * @throws java.util.NoSuchElementException
            *          if there are no more tokens in this tokenizer"s string.
            */
           public String nextToken(final String theSeparator)
           {
             separator = theSeparator;
             return nextToken();
           }
           /**
            * Returns the same value as the hasMoreTokens method. It exists so that this class can implement the
            * Enumeration interface.
            *
            * @return true if there are more tokens; false otherwise.
            * @see java.util.Enumeration
            * @see org.jfree.report.util.CSVTokenizer#hasMoreTokens()
            */
           public boolean hasMoreElements()
           {
             return hasMoreTokens();
           }
           /**
            * Returns the same value as the nextToken method, except that its declared return value is
            * Object rather than String. It exists so that this class can implement the
            * Enumeration interface.
            *
            * @return the next token in the string.
            * @throws java.util.NoSuchElementException
            *          if there are no more tokens in this tokenizer"s string.
            * @see java.util.Enumeration
            * @see org.jfree.report.util.CSVTokenizer#nextToken()
            */
           public Object nextElement()
           {
             return nextToken();
           }
           /**
            * Calculates the number of times that this tokenizer"s nextToken method can be called before it
            * generates an exception. The current position is not advanced.
            *
            * @return the number of tokens remaining in the string using the current delimiter set.
            * @see org.jfree.report.util.CSVTokenizer#nextToken()
            */
           public int countTokens()
           {
             int count = 0;
             final int preserve = this.currentIndex;
             final boolean preserveStart = this.beforeStart;
             while (this.hasMoreTokens())
             {
               this.nextToken();
               count++;
             }
             this.currentIndex = preserve;
             this.beforeStart = preserveStart;
             return count;
           }
           /**
            * Returns the quate.
            *
            * @return char
            */
           public String getQuate()
           {
             return this.quate;
           }
           /**
            * Sets the quate.
            *
            * @param quate The quate to set
            */
           public void setQuate(final String quate)
           {
             this.quate = quate;
           }
          

          }


           </source>
             
            
           
            
          



          Writes CSV (Comma Separated Value) files

             <source lang="java">
           
          

          /*------------------------------------------------------------------------------ Name: CSVWriter.java Project: jutils.org Comment: writes CSV (Comma Separated Value) files Version: $Id: CSVWriter.java,v 1.2 2004/04/07 08:04:24 laurent Exp $ Author: Roedy Green roedy@mindprod.ru, Heinrich Goetzger goetzger@gmx.net


          */

          import java.io.EOFException; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; /**

          * Writes CSV (Comma Separated Value) files.
          *
          * This format is mainly used my Microsoft Word and Excel.
          * Fields are separated by commas, and enclosed in
          * quotes if they contain commas or quotes.
          * Embedded quotes are doubled.
          * Embedded spaces do not normally require surrounding quotes.
          * The last field on the line is not followed by a comma.
          * Null fields are represented by two commas in a row.
          *
          * @author copyright (c) 2002 Roedy Green  Canadian Mind Products
          * Roedy posted this code on Newsgroups:comp.lang.java.programmer on 27th March 2002.
          *
          * Heinrich added some stuff like comment ability and linewise working.
          *
          */
          

          public class CSVWriter {

            /**
             * Constructor
             *
             * @param pw     PrintWriter where fields will be written.
             * @param forceQuotes
             *               true if you want all fields surrounded in quotes,
             *               whether or not they contain commas, quotes or spaces.
             * @param separator
             *               field separator character, usually "," in North America,
             *               ";" in Europe and sometimes "\t" for tab.
             * @param lineSeparator
             *               gives the delimiter for the line; is per default set to
             *               the system property "line.separator"
             */
            public CSVWriter(PrintWriter pw, boolean forceQuotes, char separator, String lineSeparator) {
               this.pw = pw;
               this.forceQuotes = forceQuotes;
               this.separator = separator;
               this.rument = "# ";
               this.lineSeparator = lineSeparator;
            } // end of CSVWriter
            public CSVWriter(Writer w, boolean forceQuotes, char separator, String lineSeparator) {
                this(new PrintWriter(w),forceQuotes,separator,lineSeparator);
            }
            /**
             * Constructor with default field separator ",".
             *
             * @param pw     PrintWriter where fields will be written.
             */
            public CSVWriter(PrintWriter pw) {
               this.pw = pw;
               this.forceQuotes = false;
               this.separator = ",";
               this.rument = "# ";
               this.lineSeparator = System.getProperty("line.separator");
            } // end of CSVWriter
             
            public CSVWriter(Writer w) {
                this(new PrintWriter(w));
            }
            /**
             * Constructor with default field separator ",".
             *
             * @param pw     PrintWriter where fields will be written.
             * @param comment Character used to start a comment line
             */
            public CSVWriter(PrintWriter pw, char comment) {
               this.pw = pw;
               this.forceQuotes = false;
               this.separator = ",";
               this.rument = String.valueOf(comment) + " ";
               this.lineSeparator = System.getProperty("line.separator");
            } // end of CSVWriter
            public CSVWriter(Writer w, char comment) {
                this(new PrintWriter(w),comment);
            }
            /**
             * PrintWriter where CSV fields will be written.
             */
            PrintWriter pw;
            /**
             * true if you want all fields surrounded in quotes,
             * whether or not they contain commas, quotes or spaces.
             */
            boolean forceQuotes;
            /*
             * field separator character, usually "," in North America,
             * ";" in Europe and sometimes "\t" for tab.
             */
            char separator;
            /**
             * true if there has was a field previously written to
             * this line, meaning there is a comma pending to
             * be written.
             */
            boolean wasPreviousField = false;
            /**
             * Character to start a comment line with. May be "#" for example.
             */
            String comment;
            /**
             * Line separator.
             */
            String lineSeparator;
            /**
             * Writes a single coment line to the file given by the text.
             * This is the text leaded by the comment char + " ", given in the constructor.
             * @param text contains the comment text.
             */
            public void writeCommentln(String text) {
               if (wasPreviousField) writeln(); // close open line since we need to start a new one for comment
               pw.print(comment);
               //wasPreviousField = false; // to prevent a comma after the comment sign
               write(text);
               writeln();
            } // end of writeComentln
            /**
             * Writes a single value in a line suited by a newline to the file given by the token.
             * @param token contains the value.
             */
            public void writeln(String token) {
               write(token);
               writeln();
            } // end of writeln
            /**
             * Writes a new line in the CVS output file to demark the end of record.
             */
            public void writeln() {
               /* don"t bother to write last pending comma on the line */
               wasPreviousField = false;
               pw.print(lineSeparator);
            } // end of writeln
            /**
             * Writes a single line of comma separated values from the array given by line.
             * @param line containig an array of tokens.
             */
            public void writeln(String[] line) {
               for(int ii=0; ii < line.length; ii++) {
                  write(line[ii]);
               } // end of for
               writeln(); // write newLine
            } // end of writeln
            /**
              * Write one csv field to the file, followed by a separator
              * unless it is the last field on the line. Lead and trailing
              * blanks will be removed.
              *
              * @param s      The string to write.  Any additional quotes or
              *               embedded quotes will be provided by write.
              */
            public void write(String s) {
               if ( wasPreviousField ) {
                  pw.print(separator);
               }
               if ( s == null ) {
                  pw.print("");
                  return;
               } // end of if s == null
               s = s.trim();
               if ( s.indexOf("\"") >= 0 ) {
                  /* worst case, needs surrounding quotes and internal quotes doubled */
                  pw.print ("\"");
                  for ( int i=0; i<s.length(); i++ ) {
                     char c = s.charAt(i);
                     if ( c == "\"" ) {
                        pw.print("\"\"");
                     } else {
                        pw.print(c);
                     }
                  }
                  pw.print ("\"");
                  // end of if \"
               } else if ( s.indexOf("\n") >=0 ) {
                  // bad case as well: having a new line in the token: \n
                  pw.print ("\"");
                  for ( int i=0; i<s.length(); i++ ) {
                     char c = s.charAt(i);
                     if ( c == "\n" ) {
                        pw.print("\\n");
                     } else {
                        pw.print(c);
                     }
                  }
                  pw.print ("\"");
                  // end of if \n
               } else if ( forceQuotes || s.indexOf(separator) >= 0 ) {
                  /* need surrounding quotes */
                  pw.print ("\"");
                  pw.print(s);
                  pw.print ("\"");
               } else {
                  /* ordinary case, no surrounding quotes needed */
                  pw.print(s);
               }
               /* make a note to print trailing comma later */
               wasPreviousField = true;
            } // end of write
            /**
             * Close the PrintWriter.
             */
            public void close() {
               if ( pw != null ) {
                  pw.close();
                  pw = null;
               } // end of if
            } // end of close
            /**
             * Test driver
             *
             * @param args  [0]: The name of the file.
             */
            static public void main(String[] args) {
               try {
                  // write out a test file
                  PrintWriter pw = new PrintWriter( new FileWriter(args[0]));
                  CSVWriter csv = new CSVWriter(pw, false, ",", System.getProperty("line.separator") );
                  csv.writeCommentln("This is a test csv-file: "" + args[0] + """);
                  csv.write("abc");
                  csv.write("def");
                  csv.write("g h i");
                  csv.write("jk,l");
                  csv.write("m\"n\"o ");
                  csv.writeln();
                  csv.write("m\"n\"o ");
                  csv.write("    ");
                  csv.write("a");
                  csv.write("x,y,z");
                  csv.write("x;y;z");
                  csv.writeln();
                  csv.writeln(new String[] {"This", "is", "an", "array."});
                  csv.close();
               } catch ( IOException  e ) {
                  e.printStackTrace();
                  System.out.println(e.getMessage());
               }
            } // end main
          

          } // end CSVWriter // end of file


           </source>