Skip to content

Instantly share code, notes, and snippets.

@msdotnetclr
Created February 5, 2018 16:54
Show Gist options
  • Save msdotnetclr/187ccc5c1415980e2a48d246c54bae62 to your computer and use it in GitHub Desktop.
Save msdotnetclr/187ccc5c1415980e2a48d246c54bae62 to your computer and use it in GitHub Desktop.
NVV CSV Classes
/*****************************************************/
/*** Copyright (c) 2014 Vladimir Nikitenko ***/
/*** Code Project Open License (CPOL) ***/
/*** (http://www.codeproject.com/info/cpol10.aspx) ***/
/*****************************************************/
/***** NvvCSVClasses.cs *****
===== History:
----- Version 1.1 - current (2014-09-03)
- Namespace changed
- Significant performance improvement:
- Use of StringBuilder where it is appropriate
- Assembled frequently called methods/procedures into big procedure at expense of code
structuring and readability. Apparently time of procedural call is significant.
----- Version 1.0 – initial release (2014-05-19)
******************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Nvv.IO.CSV.Cs
{
// Classes CSVReader, CSVFileReader and CSVStringReader work with CSV formatted data as it defined
// in “RFC 4180. Common Format and MIME Type for Comma-Separated Values (CSV) Files”:
// http://tools.ietf.org/html/rfc4180
// According to Wikipedia, “A general standard for the CSV file format does not exist, but
// RFC 4180 provides a de facto standard for some aspects of it.”
// While mentioned classes are RFC4180-compliant, they provide (public) properties
// that allow handling of other existing variations of CSV format as well.
public class CSVField
{
private string _Value; //field value in original format, i.e. string - as it is in CSV file
private string _Name;
public CSVField()
{
_Value = "";
_Name = "";
}
public string Value
{
get { return _Value; }
set
{
if (value != null)
_Value = value;
else
_Value = "";
}
}
public string Name
{
get { return _Name; }
set
{
if (value != null)
_Name = value;
else
_Name = "";
}
}
}//class CSVField
public abstract class CSVReader : IDisposable
{
#region Private (region...)
private enum CSVParserState
{
ChkBegOfRecord,
ChkBegOfField,
NotQuoted,
Quoted,
ChkIntrnQuote,
ChkAfterCR
}
private enum CSVParserEvent
{
DataChar,
Quote,
FieldSeparator,
CR,
LF,
Eof
}
private bool _Active; //true when file is Open
private List<CSVField> _Fields;
private CSVFields _FieldsAsIndexedProperty;
private TextReader _CSVTextReader;
private bool _Bof;
private bool _Eof;
private bool _HeaderPresent;
private bool _ReadingHeader;
private bool _Flag_ExitMainLoop;
private int _IndexOfLastProcessedField; //zero-based
private int _IndexOfLastProcessedRecord; //zero-based
private StringBuilder _Buffer_FieldValue;
private CSVParserState _ParserState;
private char _Buffer_ReadFromFile;
private bool _IgnoreSpecialCharacters;
private bool _UseFieldQuoting;
private bool _IgnoreEmptyLines;
private bool _FieldCount_AutoDetect;
private bool _FieldCount_AutoDetect_InProgress;
private char _FieldSeparatorChar;
private char _QuoteChar;
private bool _ASCIIonly;
private void DoOpen()
{
try
{
Reset_for_Close();//initial state is set here
OpenCSVSourceAndCreateStream();
_Bof = true;
_Eof = (_CSVTextReader.Peek() < 0);
FieldCount_AutoDetect_InProgress = FieldCount_AutoDetect && (!Eof);
if (HeaderPresent && (!Eof))
{
_ReadingHeader = true;
try
{
Next();
}
finally
{
_ReadingHeader = false;
}
}
if (!Eof)
Next();
}
catch (Exception)
{
DoClose();
throw;
}
}// DoOpen()
private void DoClose()
{
Reset_for_Close();
}
private void OpenCSVSourceAndCreateStream()
{
CloseCSVSourceAndDestroyStream();
_CSVTextReader = CreateDataSourceReader();
}
private void CloseCSVSourceAndDestroyStream()
{
if (_CSVTextReader != null)
{
_CSVTextReader.Dispose();
_CSVTextReader = null;
}
}
private void ParsingLoop_Main()
{
CSVParserEvent parserEvent;
bool readingComplete;
Int32 charAsInt32;
CSVParserState switchToState;
while (!_Flag_ExitMainLoop)
{
// GetEvent (begin)
parserEvent = CSVParserEvent.Eof; //just init with something to get rid of compiler error "Use of unassigned..."
do
{
readingComplete = true;
charAsInt32 = _CSVTextReader.Read();
if (charAsInt32 < 0)
{// reached end of the stream
_Buffer_ReadFromFile = (char)0; // just in case put "empty" char there
parserEvent = CSVParserEvent.Eof;
}
else
{
_Buffer_ReadFromFile = (char)charAsInt32;
if (_Buffer_ReadFromFile == CR)
parserEvent = CSVParserEvent.CR;
else if (_Buffer_ReadFromFile == LF)
parserEvent = CSVParserEvent.LF;
else if (_Buffer_ReadFromFile == _FieldSeparatorChar)
parserEvent = CSVParserEvent.FieldSeparator;
else if (_Buffer_ReadFromFile == _QuoteChar)
parserEvent = CSVParserEvent.Quote;
else if ((_Buffer_ReadFromFile >= CharCode0x20) &&
((!ASCIIonly) || (ASCIIonly && (_Buffer_ReadFromFile <= CharCode0x7E))))
parserEvent = CSVParserEvent.DataChar;
else if (IgnoreSpecialCharacters)
readingComplete = false;
else
Throw_ErrorWithRecordAndFieldNum(
"Invalid character (HexCode = " + ((int)_Buffer_ReadFromFile).ToString("X8") +
"). Input file is invalid.");
}
} while (!readingComplete);
// GetEvent (end)
switchToState = CSVParserState.ChkBegOfRecord; // just to get rid of compiler's error "Use of unassigned ..."
switch (_ParserState)
{
case CSVParserState.ChkBegOfRecord:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
Handle_DataChar();
_ParserState = CSVParserState.NotQuoted;
break;
case CSVParserEvent.Quote:
Handle_QuoteAtBegOfValue(ref switchToState);
_ParserState = switchToState;
break;
case CSVParserEvent.FieldSeparator:
// empty value
DoEndOfField();
_ParserState = CSVParserState.ChkBegOfField;
break;
case CSVParserEvent.CR:
// empty line
Handle_EmptyLine();
_ParserState = CSVParserState.ChkAfterCR;
break;
case CSVParserEvent.LF:
//empty line that delimited by single LF
Handle_EmptyLine();
//not changing state
break;
case CSVParserEvent.Eof:
//Possible situations:
// 1. Last line of file has CR-LF before Eof.
// 2. Last line without CR-LF before Eof containing empty value in
// the file with one field per record.
// There is no way to differentiate them. Therefore let's assume that
// it is always situation "1".
Handle_EndOfFileAtBegOfRecord();
// Not changing state. BTW it does not matter here
break;
}// switch (parserEvent)
break;
case CSVParserState.ChkBegOfField:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
Handle_DataChar();
_ParserState = CSVParserState.NotQuoted;
break;
case CSVParserEvent.Quote:
Handle_QuoteAtBegOfValue(ref switchToState);
_ParserState = switchToState;
break;
case CSVParserEvent.FieldSeparator:
//empty value
DoEndOfField();
//Not changing state
break;
case CSVParserEvent.CR:
//empty value
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkAfterCR;
break;
case CSVParserEvent.LF:
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord;
break;
case CSVParserEvent.Eof:
//empty value
Handle_EndOfFileAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord; //actually does not matter
break;
}
break;
case CSVParserState.NotQuoted:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
case CSVParserEvent.Quote:
Handle_DataChar();
//Not changing state
break;
case CSVParserEvent.FieldSeparator:
DoEndOfField();
_ParserState = CSVParserState.ChkBegOfField;
break;
case CSVParserEvent.CR:
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkAfterCR;
break;
case CSVParserEvent.LF:
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord;
break;
case CSVParserEvent.Eof:
Handle_EndOfFileAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord;
break;
}
break;
case CSVParserState.Quoted:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
case CSVParserEvent.FieldSeparator:
case CSVParserEvent.CR:
case CSVParserEvent.LF:
Handle_DataChar();
//Not changing state
break;
case CSVParserEvent.Quote:
_ParserState = CSVParserState.ChkIntrnQuote;
break;
case CSVParserEvent.Eof:
_ParserState = CSVParserState.ChkBegOfRecord; //probably does not matter
Throw_ErrorWithRecordAndFieldNum(MsgStr_WrongFieldValueFormat);
break;
}
break;
case CSVParserState.ChkIntrnQuote:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
//if quote was closing quote then here we expect field separator,
// end of line or end of file
_ParserState = CSVParserState.ChkBegOfRecord; //probably does not matter
Throw_ErrorWithRecordAndFieldNum(MsgStr_WrongFieldValueFormat);
break;
case CSVParserEvent.Quote:
//internal quote character
Handle_DataChar();
_ParserState = CSVParserState.Quoted;
break;
case CSVParserEvent.FieldSeparator:
DoEndOfField();
_ParserState = CSVParserState.ChkBegOfField;
break;
case CSVParserEvent.CR:
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkAfterCR;
break;
case CSVParserEvent.LF:
Handle_EndOfLineAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord;
break;
case CSVParserEvent.Eof:
Handle_EndOfFileAfterLastFieldInRecord();
_ParserState = CSVParserState.ChkBegOfRecord;
break;
}
break;
case CSVParserState.ChkAfterCR:
switch (parserEvent)
{
case CSVParserEvent.DataChar:
Handle_DataChar();
_ParserState = CSVParserState.NotQuoted;
break;
case CSVParserEvent.Quote:
Handle_QuoteAtBegOfValue(ref switchToState);
_ParserState = switchToState;
break;
case CSVParserEvent.FieldSeparator:
//empty value
DoEndOfField();
_ParserState = CSVParserState.ChkBegOfField;
break;
case CSVParserEvent.CR:
//empty line
Handle_EmptyLine();
//Not changing state
break;
case CSVParserEvent.LF:
//Line delimiter <CR><LF>
_ParserState = CSVParserState.ChkBegOfRecord;
break;
case CSVParserEvent.Eof:
//Same situation as in state ChkBegOfRecord (see more in State_BegOfRecord_Handler)
Handle_EndOfFileAtBegOfRecord();
_ParserState = CSVParserState.ChkBegOfRecord; //actually does not matter
break;
}
break;
}// switch (_ParserState)
} //while (! _Flag_ExitMainLoop)
}//ParsingLoop_Main()
private void ClearFieldDataInaccessibleWhileFileClosed()
{
ClearFieldDataInaccessibleWhileEof();
for (int i = 0; i <= FieldCount - 1; i++)
Fields[i].Name = "";
}
private void ClearFieldDataInaccessibleWhileEof()
{
for (int i = 0; i <= FieldCount - 1; i++)
Fields[i].Value = "";
}
private void Reset_for_Close()
{
_Buffer_FieldValue = new StringBuilder();
FieldCount_AutoDetect_InProgress = false;
Reset_for_EndOfFile();
Reset_for_NextRecord();
Reset_for_NextField();
ClearFieldDataInaccessibleWhileFileClosed();
_IndexOfLastProcessedRecord = -1; //zero-based
_Bof = false;
_Eof = false;
_ParserState = CSVParserState.ChkBegOfRecord;
}
private void Reset_for_EndOfFile()
{
CloseCSVSourceAndDestroyStream();
ClearFieldDataInaccessibleWhileEof();
}
private void Reset_for_NextRecord()
{
_IndexOfLastProcessedField = -1;
_IndexOfLastProcessedRecord++;
}
private void Reset_for_NextField()
{
_Buffer_FieldValue.Clear();
}
private void DoEndOfFile()
{
_Eof = true;
Reset_for_EndOfFile();
_Flag_ExitMainLoop = true;
}
private void AddTo_Buffer_FieldValue(char aChar)
{
_Buffer_FieldValue.Append(aChar);
}
private void Handle_DataChar()
{
AddTo_Buffer_FieldValue(_Buffer_ReadFromFile);
}
private void Handle_QuoteAtBegOfValue(ref CSVParserState aSwitchToState)
{
if (UseFieldQuoting)
aSwitchToState = CSVParserState.Quoted;
else
{
AddTo_Buffer_FieldValue(_Buffer_ReadFromFile);
aSwitchToState = CSVParserState.NotQuoted;
}
}
private void Handle_EndOfLine()
{
// in exact order:
DoEndOfField();
DoEndOfLine();
}
private void Handle_EndOfLineAfterLastFieldInRecord()
{
Handle_EndOfLine();
}
private void Handle_EmptyLine()
{
if (!IgnoreEmptyLines)
Handle_EndOfLine();
}
private void Handle_EndOfFileAtBegOfRecord()
{
DoEndOfFile();
}
private void Handle_EndOfFileAfterLastFieldInRecord()
{
/* We cannot handle this situation as Eof (and clear field values) because current Next is
* reading this last record and it is not read by user yet.
* Therefore let's delay setting of "our" Eof until next Next.
* In other words, we are simulating end of this line and Eof at the beginning of "next empty"
* line instead. As a result, we will always "have" situation that last line has CR/LF. */
Handle_EndOfLineAfterLastFieldInRecord();
}
private bool FieldCount_AutoDetect_InProgress
{
get { return _FieldCount_AutoDetect_InProgress; }
set
{
if (_FieldCount_AutoDetect_InProgress != value)
{
_FieldCount_AutoDetect_InProgress = value;
if (_FieldCount_AutoDetect_InProgress)
FieldCount = 0;
}
}
}// FieldCount_AutoDetect_InProgress
#endregion
#region Protected (region...)
protected const char CR = (char)0x0D;
protected const char LF = (char)0x0A;
protected const char DQUOTE = (char)0x22; // double quote
protected const char COMMA = (char)0x2C;
protected const char CharCode0x20 = (char)0x20;
protected const char CharCode0x7E = (char)0x7E;
protected const string MsgStr_OperationNotAllowedInActiveState = "Operation is not allowed in Active state";
protected const string MsgStr_OperationNotAllowedInInactiveState = "Operation is not allowed in Inactive state";
protected const string MsgStr_WrongNumberOfFields = "Wrong number of fields.";
protected const string MsgStr_WrongFieldValueFormat = "Wrong format of field value.";
//Derived classes must override/implement CreateDataSourceReader returning instance of appropriate
//reader: StreamReader if CSV data is in a file, StringReader if is in string and so on
protected abstract TextReader CreateDataSourceReader();
protected virtual void DoEndOfField()
{
if (FieldCount_AutoDetect_InProgress)
FieldCount = FieldCount + 1;
if (_IndexOfLastProcessedField >= (FieldCount - 1))
Throw_ErrorWithRecordAndFieldNum(MsgStr_WrongNumberOfFields);
else
{
_IndexOfLastProcessedField++;
if (_ReadingHeader)
Fields[_IndexOfLastProcessedField].Name = _Buffer_FieldValue.ToString();
else
Fields[_IndexOfLastProcessedField].Value = _Buffer_FieldValue.ToString();
Reset_for_NextField();
}
}// DoEndOfField()
protected virtual void DoEndOfLine()
{
if (_IndexOfLastProcessedField != (FieldCount - 1))
Throw_ErrorWithRecordAndFieldNum(MsgStr_WrongNumberOfFields);
else
{
Reset_for_NextRecord();
_Flag_ExitMainLoop = true;
}
if (FieldCount_AutoDetect_InProgress)
{
FieldCount_AutoDetect_InProgress = false;
OnFieldCountAutoDetectCompleted();
}
}// DoEndOfLine()
protected virtual bool AllowFieldCountChangeEvenInOpenState()
{
return FieldCount_AutoDetect_InProgress;
}
protected virtual void OnFieldCountAutoDetectCompleted()
{
EventHandler tmpHandler = FieldCountAutoDetectCompleted;
if (tmpHandler != null)
tmpHandler(this, EventArgs.Empty);
}
protected void Throw_ErrorWithNoParam(string aMsg)
{
throw new Exception(this.GetType().Name + ":\n" + aMsg);
}
protected void Throw_ErrorWithRecordAndFieldNum(string aMsg)
{
Throw_ErrorWithNoParam(aMsg + "\n" +
"Record #: " + Convert.ToString(_IndexOfLastProcessedRecord
+ 1/*in process*/ + 1/*make it 1-based*/ ) +
"; Field #: " + Convert.ToString(_IndexOfLastProcessedField
+ 1/*in process*/ + 1/*make it 1-based*/ ) +
" (both start from 1)");
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
Close(); //This disposes ("managed") TextReader instance (which may use unmanaged resources) if it's not done yet
}
protected TextReader CSVTextReader { get { return _CSVTextReader; } }
#endregion
#region Public (region...)
// Class CSVFields to define indexed property Fields for class CSVReader
public class CSVFields
{
readonly CSVReader _ContainerClass;
internal CSVFields(CSVReader aClass) // constructor
{
_ContainerClass = aClass;
}
public CSVField this[int i]
{
get { return _ContainerClass._Fields[i]; }
}
}//class CSVFields
public CSVReader() //constructor
{
// property default values
_FieldCount_AutoDetect = true;
_UseFieldQuoting = true;
_IgnoreEmptyLines = false;
_IgnoreSpecialCharacters = false;
_FieldSeparatorChar = COMMA;
_QuoteChar = DQUOTE;
_ASCIIonly = false;
_Fields = new List<CSVField>();
_FieldsAsIndexedProperty = new CSVFields(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Open()
{
Active = true;
}
public void Close()
{
Active = false;
}
public void Next()
{
if (Active)
{
if (!Eof)
{
_Flag_ExitMainLoop = false;
try
{
ParsingLoop_Main();
_Bof = false;
}
catch
{
Close(); //<----- Close if error during parsing
throw;
}
}
else
{
//Reading “beyond” end of file. Next does nothing.
//Field values will state cleared, which is done at setting Eof.
}
}
else
Throw_ErrorWithNoParam("Getting next record.\n" + MsgStr_OperationNotAllowedInInactiveState);
}//Next()
#region Input parameters (region...)
// --------------------------------------- Input parameters (begin)
// Attempt to change input parameter values in active state causes exception
/*HeaderPresent:
- False (by default): All records are considered as records with values.
- True: Values in the very first record are considered as field names. */
public bool HeaderPresent
{
get { return _HeaderPresent; }
set
{
if (_HeaderPresent != value)
if (!Active)
_HeaderPresent = value;
else
Throw_ErrorWithNoParam("Modifying HeaderPresent input parameter.\n" + MsgStr_OperationNotAllowedInActiveState);
}
}//HeaderPresent
/* FieldCount auto detection is done during/within Open on the base of very
first record (or on the base of first non-empty record when
IgnoreEmptyLines==true) and is accessible after Open (Active = true) is complete */
public bool FieldCount_AutoDetect
{
get { return _FieldCount_AutoDetect; }
set
{
if (_FieldCount_AutoDetect != value)
{
if (!Active)
_FieldCount_AutoDetect = value;
else
Throw_ErrorWithNoParam("Modifying FieldCount_AutoDetect input parameter.\n" + MsgStr_OperationNotAllowedInActiveState);
}
}
}//FieldCount_AutoDetect
/* FieldCount is always >= 0. Attempt to assign negative value is ignored.
If FieldCount_AutoDetect is true then assigned value is meaningless and will be replaced during Open(). */
public int FieldCount
{
get { return _Fields.Count; }
set
{
if ((value >= 0) && (FieldCount != value))
if ((!Active) || (Active && AllowFieldCountChangeEvenInOpenState()))
{
if (value > FieldCount)
for (int i = FieldCount; i <= value - 1; i++)
_Fields.Add(new CSVField());
else// value < FieldCount
for (int i = FieldCount - 1; i >= value; i--)
_Fields.RemoveAt(i);
}
else
Throw_ErrorWithNoParam("Modifying field count.\n" + MsgStr_OperationNotAllowedInActiveState);
}
}//FieldCount
/* FieldSeparatorCharCode:
- It is a code (!) and not a char. Virtually any (see also property ASCIIonly) unicode character
including special characters like TAB, etc. can be used as field separator.*/
public int FieldSeparatorCharCode
{
get { return Convert.ToInt32(_FieldSeparatorChar); }
set
{
if (_FieldSeparatorChar != (char)value)
if (!Active)
_FieldSeparatorChar = (char)value;
else
Throw_ErrorWithNoParam("Modifying FieldSeparatorCharCode input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// FieldSeparatorCharCode
/* UseFieldQuoting
- False: In this case it is assumed that "quote char" is never used for field value surrounding and
is considered as ordinary data character provided that code is in data char code range,
i.e. not special character. In other words, value specified as QuoteCharCode (see below) is meaningless.
- True (by default): Field value may or may not be enclosed in characters specified in QuoteCharCode (see below). */
public bool UseFieldQuoting
{
get { return _UseFieldQuoting; }
set
{
if (_UseFieldQuoting != value)
if (!Active)
_UseFieldQuoting = value;
else
Throw_ErrorWithNoParam("Modifying UseFieldDblQuoting input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// UseFieldQuoting
/* QuoteCharCode:
- It is a code (!) and not a char. Virtually any (see also property ASCIIonly) unicode character
can be used as “quote char”. It is assumed that this character is also used as escape character */
public int QuoteCharCode
{
get { return Convert.ToInt32(_QuoteChar); }
set
{
if (_QuoteChar != (char)value)
if (!Active)
_QuoteChar = (char)value;
else
Throw_ErrorWithNoParam("Modifying QuoteCharCode input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// QuoteCharCode
/* IgnoreEmptyLines:
- False (by default): Presence of empty lines in source, which is indication of wrong input
data format, causes exception.
- True: Empty lines are ignored. */
public bool IgnoreEmptyLines
{
get { return _IgnoreEmptyLines; }
set
{
if (_IgnoreEmptyLines != value)
if (!Active)
_IgnoreEmptyLines = value;
else
Throw_ErrorWithNoParam("Modifuing IgnoreEmptyLines input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// IgnoreEmptyLines
/* ASCIIOnly:
- False (by default): Full Unicode range of characters is handled. Characters with codes less than 0x20 are
considered as “special characters” (see property IgnoreSpecialCharacters below).
- True: Only ASCII range of characters is handled. Characters with codes outside range 0x20 – 0x7E are
considered as “special characters” (see property IgnoreSpecialCharacters below). */
public bool ASCIIonly
{
get { return _ASCIIonly; }
set
{
if (_ASCIIonly != value)
if (!Active)
_ASCIIonly = value;
else
Throw_ErrorWithNoParam("Modifying ASCIIonly input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// ASCIIonly
/* IgnoreSpecialCharacters
- False (by default): Presence of “special characters”, as they defined above in property ASCIIonly
description, causes exception. This does not affect line breaks, field separator and
quote characters even if last two are from the “special character” range.
- True: “Special characters” are ignored except line breaks, field separator and quote characters even if
last two are from the “special character” range. */
public bool IgnoreSpecialCharacters
{
get { return _IgnoreSpecialCharacters; }
set
{
if (_IgnoreSpecialCharacters != value)
if (!Active)
_IgnoreSpecialCharacters = value;
else
Throw_ErrorWithNoParam("Modifying IgnoreSpecialCharacters input parameter.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// IgnoreSpecialCharacters
// ---------------------------- Input parameters (end)
#endregion
public bool Active
{
get { return _Active; }
set
{
if (_Active != value)
{
_Active = value;
if (_Active)
try
{
DoOpen();
}
catch (Exception)
{
_Active = false;
DoClose();
throw;
}
else
DoClose();
}
}// set
}//Active
public bool Bof { get { return _Bof; } }
public bool Eof { get { return _Eof; } }
//{Fields is "zero-based" ( 0 <= Index <= FieldCount-1)}
public CSVFields Fields { get { return _FieldsAsIndexedProperty; } }
public int RecordCountProcessedSoFar
{
get
{
if (_IndexOfLastProcessedRecord < 0)
return 0;
else
return _IndexOfLastProcessedRecord + 1; //_IndexOfLastProcessedRecord is zero-based
}
}
public event EventHandler FieldCountAutoDetectCompleted;
#endregion
}//class CSVReader
//================== class CSVFileReader ==================
public class CSVFileReader : CSVReader
{
private string _FileName;
protected override TextReader CreateDataSourceReader()
{
return new StreamReader(_FileName);
}
public CSVFileReader()
: base()
{
_FileName = "";
}
public string FileName
{
get { return _FileName; }
set
{
if (_FileName.ToUpper() != value.ToUpper())
if (!Active)
_FileName = value;
else
Throw_ErrorWithNoParam("Modifying input file path.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// property FileName
}// class CSVFileReader
//================== class CSVStringReader ==================
public class CSVStringReader : CSVReader
{
private string _DataString;
protected override TextReader CreateDataSourceReader()
{
return new StringReader(_DataString);
}
public CSVStringReader()
: base()
{
_DataString = "";
}
public string DataString
{
get { return _DataString; }
set
{
if (!Active)
_DataString = value;
else
Throw_ErrorWithNoParam("Modifying input CSV string.\n" +
MsgStr_OperationNotAllowedInActiveState);
}
}// property DataString
}// class CSVStringReader
}//namespace Nvv.Components.CSV
@msdotnetclr
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment