/// <summary> /// Reads the values of a single CSV formatted record into a given /// <see cref="CsvValues"/> container. /// </summary> /// <param name="aTextReader"></param> /// <param name="vValues"> /// Destination that will receive the record data. All values in this /// instance are overwritten. Missing values are overwritten with String.Empty /// references. /// </param> /// <returns> /// If a record was read, then the number of values that were actually found is /// returned. For an empty line zero is returned. If an immediate end of file was /// encountered, then -1 is returned. /// </returns> public int ReadLine(TextReader aTextReader, CsvValues vValues) { if ((aTextReader == null) || (vValues == null)) { throw new ArgumentNullException(); } Reset(); bool fMore = false; do { string sLine = aTextReader.ReadLine(); if (sLine == null) { if (fMore) { throw new FormatException("CSV truncated"); } return -1; } fMore = !ParseLine(sLine, vValues); } while (fMore); // Store the number of actual values that have been read. vValues.InternalSetActualValues(m_nValueIndex); // After splitting up all values from the given CSV line, the final nValueIndex // tells us the number of values that we actually extracted from the line. If // the values array expects more data, then it is filled with empty string values. for (int i = m_nValueIndex; i < m_aCsvFile.MaxValues; ++i) { vValues[i] = String.Empty; } // return m_nValueIndex; }
private string _GetCsvStatementData(Swift9xxBase aStmt) { StringBuilder sb = new StringBuilder(20000); sb.Append("EntryDate;ValueDate;Value;AcctNo;BankCode;Name1;Name2;PaymtPurpose;EntryText;PrimaNotaNo;TranTypeIdCode;ZkaTranCode;TextKeyExt;BankRef;OwnerRef;SupplementaryDetails"); sb.Append(Environment.NewLine); foreach (SwiftStatementLine aStmtLine in aStmt.StatementLines) { CsvValues aCsv = new CsvValues(16); if (!aStmtLine.EntryDate.IsNull) { aCsv[0] = aStmtLine.EntryDate.ToString(SwiftDateFormat.StandardDate); } if (!aStmtLine.ValueDate.IsNull) { aCsv[1] = aStmtLine.ValueDate.ToString(SwiftDateFormat.StandardDate); } aCsv[2] = SwiftAmt.Format(aStmtLine.DecValue, ',', 2); aCsv[3] = aStmtLine.PayeePayerAcctNo; aCsv[4] = aStmtLine.PayeePayerBankCode; aCsv[5] = aStmtLine.PayeePayerName1; aCsv[6] = aStmtLine.PayeePayerName2; string[] vsPaymtPurpose = aStmtLine.PaymtPurpose; if (vsPaymtPurpose != null) { aCsv[7] = String.Join("|", vsPaymtPurpose); } aCsv[8] = aStmtLine.EntryText; aCsv[9] = aStmtLine.PrimaNotaNo; aCsv[10] = aStmtLine.TranTypeIdCode; aCsv[11] = aStmtLine.ZkaTranCode; aCsv[12] = aStmtLine.TextKeyExt; aCsv[13] = aStmtLine.BankRef; aCsv[14] = aStmtLine.OwnerRef; aCsv[15] = aStmtLine.SupplementaryDetails; sb.Append(aCsv); sb.Append(Environment.NewLine); } return sb.ToString(); }
/// <summary> /// /// </summary> /// <param name="sLine"></param> /// <param name="vValues"></param> /// <returns> /// If the final state after parsing the line indicates the end of the record, /// then true is returned. /// </returns> internal bool ParseLine(string sLine, CsvValues vValues) { int nMaxValues = m_aCsvFile.MaxValues; char chComma = m_aCsvFile.Comma; char chQuote = m_aCsvFile.Quote; int nLineLength = sLine.Length; for (int i = 0; i <= nLineLength; ++i) { int ch = (i == nLineLength) ? -1 : sLine[i]; // Process character according to state machine. switch (m_nState) { // StartValue - This is the initial state which is reentered every time when // we await another field value. case ParseState.StartValue: if (ch == chQuote) { m_nState = ParseState.QuotedValue; } else if (ch == ' ') { // m_nState = ParseState.StartValue; } else if (ch == chComma) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = String.Empty; } ++m_nValueIndex; // m_nState = ParseState.StartValue; } else if (ch == -1) { m_nState = ParseState.Done; } else { m_sb.Append((char)ch); m_nState = ParseState.VanillaValue; } break; // VanillaValue - The characters of an unquoted value are collected until a // comma or the end of the line is encountered. case ParseState.VanillaValue: if ((ch == chComma) || (ch == -1)) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString().TrimEnd(' '); } ++m_nValueIndex; m_sb.Length = 0; m_nState = (ch == -1) ? ParseState.Done : ParseState.StartValue; } else { m_sb.Append((char)ch); // m_nState = ParseState.VanillaValue; } break; // QuotedValue - The characters of a quoted value are collected. If another // quote is encountered it could be a quoted quote or the value could end. case ParseState.QuotedValue: if (ch == chQuote) { m_nState = ParseState.QuoteInQuotedValue; } else if (ch == -1) { m_sb.Append(Environment.NewLine); // m_nState = ParseState.QuotedValue; } else { m_sb.Append((char)ch); // m_nState = ParseState.QuotedValue; } break; // QuoteInQuotedValue - A quote within a quoted value was encountered. We must // now find another quote, or a comma delimiter, or the end of the line. case ParseState.QuoteInQuotedValue: if (ch == chQuote) { m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } else if ((ch == chComma) || (ch == -1)) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString(); } ++m_nValueIndex; m_sb.Length = 0; m_nState = (ch == -1) ? ParseState.Done : ParseState.StartValue; } else if (ch == ' ') { // This is either malformed, or there really is whitespace after the // closing quote. if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString(); } ++m_nValueIndex; m_sb.Length = 0; m_nState = ParseState.EndQuotedValue; } else { // This is an invalid structure. Within a quoted field, all quotes // must be doubled. Anyway we try to recover gracefully by simply // accepting the quote and the subsequent character. m_sb.Append(chQuote); m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } break; case ParseState.EndQuotedValue: if (ch == chComma) { m_nState = ParseState.StartValue; } else if (ch == -1) { m_nState = ParseState.Done; } else if (ch != ' ') { // This is an invalid structure. Only whitespace may appear until the // next field. We try to recover by backing up and continuing the // previous field. If multiple whitespaces occur after the quote, then // they will be collapsed into a single whitespace. Debug.Assert(m_nValueIndex > 0); Debug.Assert(m_sb.Length == 0); --m_nValueIndex; m_sb.Append(vValues[m_nValueIndex]); m_sb.Append(chQuote); m_sb.Append(' '); m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } break; } } // return m_nState == ParseState.Done; }
public void SetHeader(CsvValues aCsvHeader) { m_nMaxValues = aCsvHeader.ActualValues; _BuildHeaderHashtable(aCsvHeader); }
/// <summary> /// /// </summary> /// <param name="sLine"></param> /// <param name="vValues"></param> /// <returns> /// If the final state after parsing the line indicates the end of the record, /// then true is returned. /// </returns> internal bool ParseLine(string sLine, CsvValues vValues) { int nMaxValues = m_aCsvFile.MaxValues; char chComma = m_aCsvFile.Comma; char chQuote = m_aCsvFile.Quote; int nLineLength = sLine.Length; for (int i = 0; i <= nLineLength; ++i) { int ch = (i == nLineLength) ? -1 : sLine[i]; // Process character according to state machine. switch (m_nState) { // StartValue - This is the initial state which is reentered every time when // we await another field value. case ParseState.StartValue: if (ch == chQuote) { m_nState = ParseState.QuotedValue; } else if (ch == ' ') { // m_nState = ParseState.StartValue; } else if (ch == chComma) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = String.Empty; } ++m_nValueIndex; // m_nState = ParseState.StartValue; } else if (ch == -1) { m_nState = ParseState.Done; } else { m_sb.Append((char)ch); m_nState = ParseState.VanillaValue; } break; // VanillaValue - The characters of an unquoted value are collected until a // comma or the end of the line is encountered. case ParseState.VanillaValue: if ((ch == chComma) || (ch == -1)) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString().TrimEnd(' '); } ++m_nValueIndex; m_sb.Length = 0; m_nState = (ch == -1) ? ParseState.Done : ParseState.StartValue; } else { m_sb.Append((char)ch); // m_nState = ParseState.VanillaValue; } break; // QuotedValue - The characters of a quoted value are collected. If another // quote is encountered it could be a quoted quote or the value could end. case ParseState.QuotedValue: if (ch == chQuote) { m_nState = ParseState.QuoteInQuotedValue; } else if (ch == -1) { m_sb.Append(Environment.NewLine); // m_nState = ParseState.QuotedValue; } else { m_sb.Append((char)ch); // m_nState = ParseState.QuotedValue; } break; // QuoteInQuotedValue - A quote within a quoted value was encountered. We must // now find another quote, or a comma delimiter, or the end of the line. case ParseState.QuoteInQuotedValue: if (ch == chQuote) { m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } else if ((ch == chComma) || (ch == -1)) { if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString(); } ++m_nValueIndex; m_sb.Length = 0; m_nState = (ch == -1) ? ParseState.Done : ParseState.StartValue; } else if (ch == ' ') { // This is either malformed, or there really is whitespace after the // closing quote. if (m_nValueIndex < nMaxValues) { vValues[m_nValueIndex] = m_sb.ToString(); } ++m_nValueIndex; m_sb.Length = 0; m_nState = ParseState.EndQuotedValue; } else { // This is an invalid structure. Within a quoted field, all quotes // must be doubled. Anyway we try to recover gracefully by simply // accepting the quote and the subsequent character. m_sb.Append(chQuote); m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } break; case ParseState.EndQuotedValue: if (ch == chComma) { m_nState = ParseState.StartValue; } else if (ch == -1) { m_nState = ParseState.Done; } else if (ch != ' ') { // This is an invalid structure. Only whitespace may appear until the // next field. We try to recover by backing up and continuing the // previous field. If multiple whitespaces occur after the quote, then // they will be collapsed into a single whitespace. Debug.Assert(m_nValueIndex > 0); Debug.Assert(m_sb.Length == 0); --m_nValueIndex; m_sb.Append(vValues[m_nValueIndex]); m_sb.Append(chQuote); m_sb.Append(' '); m_sb.Append((char)ch); m_nState = ParseState.QuotedValue; } break; } } // return(m_nState == ParseState.Done); }
private void _BuildHeaderHashtable(CsvValues aCsvHeader) { m_vHeader = new Hashtable(); for (int i = 0; i < aCsvHeader.MaxValues; ++i) { string sColName = aCsvHeader[i]; if (!string.IsNullOrEmpty(sColName)) { // Before we add the column name, we check whether the same column name // was already used. If so, then we generate an unique column name by // appending a number. string sBaseName = sColName; int n = 2; while (m_vHeader.ContainsKey(sColName)) { sColName = sBaseName + n.ToString(CultureInfo.InvariantCulture); ++n; } // Now we have a unique column name and thus can safely add the header // entry. m_vHeader.Add(sColName, i); } } }
private int _ReadValues(CsvValues vValues) { // Read one CSV line from the StreamReader. If we encounter the end of the // StreamReader, then -1 is returned. Only if a non-empty line was read, then we // update the line number. int nValueCount = m_aParser.ReadLine(m_aReader, vValues); if (nValueCount > 0) { ++m_nLine; } return nValueCount; }
public void WriteLine(CsvValues aCsv) { Debug.Assert(m_aReader == null); Debug.Assert(m_aWriter != null); Debug.Assert(aCsv.File == this); string sLine = aCsv.ToString(m_chComma, m_chQuote); m_aWriter.WriteLine(sLine); }
/// <summary> /// Writing the CSV header creates an internal hashtable that associates the column /// indices with their column names. /// </summary> public void WriteHeader(CsvValues aCsvHeader) { Debug.Assert(m_aReader == null); Debug.Assert(m_aWriter != null); Debug.Assert(aCsvHeader.File == this); _BuildHeaderHashtable(aCsvHeader); string sLine = aCsvHeader.ToString(m_chComma, m_chQuote); m_aWriter.WriteLine(sLine); }
public CsvValues ReadLine() { Debug.Assert(m_aReader != null); Debug.Assert(m_aWriter == null); CsvValues aCsv = new CsvValues(this, m_nMaxValues); int nValueCount = _ReadValues(aCsv); return nValueCount < 0 ? null : aCsv; }
/// <summary> /// Successfully reading the CSV header creates an internal hashtable that associates /// the column indices with their column names. In addition, the /// <see cref="MaxValues"/> property is adjusted to the actual number of values that /// were found in the header line. /// </summary> public CsvValues ReadHeader() { Debug.Assert(m_aReader != null); Debug.Assert(m_aWriter == null); CsvValues aCsvHeader = new CsvValues(this, m_nMaxValues); int nMaxValues = _ReadValues(aCsvHeader); if (nMaxValues <= 0) { throw new FormatException("Csv"); } SetHeader(aCsvHeader); return aCsvHeader; }