/// <summary> /// Serialize a single row for the CSV file /// </summary> /// <param name="obj"></param> /// <returns></returns> public string Serialize(T obj) { var items = new List <object>(); foreach (var field in _fields) { items.Add(field.GetValue(obj)); } foreach (var prop in _properties) { items.Add(prop.GetValue(obj, null)); } return(CSV.ItemsToCsv(items, _settings, _riskyChars, _forceQualifierTypes)); }
/// <summary> /// Serialize the header for the CSV file /// </summary> /// <returns></returns> public string SerializeHeader() { var headers = new List <object>(); foreach (var field in _fields) { headers.Add(field.Name); } foreach (var prop in _properties) { headers.Add(prop.Name); } return(CSV.ItemsToCsv(headers, _settings, _riskyChars, _forceQualifierTypes)); }
public static string WriteToString(this DataTable dt, CSVSettings settings = null) #endif { if (settings == null) { settings = CSVSettings.CSV; } using (var ms = new MemoryStream()) { var cw = new CSVWriter(ms, settings); cw.Write(dt); var rawString = settings.Encoding.GetString(ms.ToArray()); return(CSV.RemoveByteOrderMarker(rawString)); } }
/// <summary> /// Write the data table to a stream in CSV format /// </summary> /// <param name="dt">The data table to write</param> public void Write(DataTable dt) { if (_settings.HeaderRowIncluded) { var headers = new List <object>(); foreach (DataColumn col in dt.Columns) { headers.Add(col.ColumnName); } _writer.Write(CSV.ItemsToCsv(headers, _settings, _riskyChars, _forceQualifierTypes)); _writer.Write(_settings.LineSeparator); } foreach (DataRow dr in dt.Rows) { _writer.Write(CSV.ItemsToCsv(dr.ItemArray, _settings, _riskyChars, _forceQualifierTypes)); _writer.Write(_settings.LineSeparator); } }
/// <summary> /// Write a single line to this CSV /// </summary> /// <param name="items"></param> public async Task WriteLineAsync(IEnumerable <object> items) { await _writer.WriteAsync(CSV.ItemsToCsv(items, _settings, _riskyChars, _forceQualifierTypes)); await _writer.WriteAsync(_settings.LineSeparator); }
/// <summary> /// Write a single line to this CSV /// </summary> /// <param name="items"></param> public void WriteLine(IEnumerable <object> items) { _writer.Write(CSV.ItemsToCsv(items, _settings, _riskyChars, _forceQualifierTypes)); _writer.Write(_settings.LineSeparator); }
/// <summary> /// Deserialize a CSV file into a list of typed objects /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public List <T> Deserialize <T>(bool ignore_dimension_errors = false, bool ignore_bad_columns = false, bool ignore_type_conversion_errors = false) where T : class, new() { List <T> result = new List <T>(); Type return_type = typeof(T); // Read in the first line - we have to have headers! string[] first_line = CSV.ParseMultiLine(_instream, _delimiter, _text_qualifier); if (first_line == null) { return(result); } int num_columns = first_line.Length; // Determine how to handle each column in the file - check properties, fields, and methods Type[] column_types = new Type[num_columns]; TypeConverter[] column_convert = new TypeConverter[num_columns]; PropertyInfo[] prop_handlers = new PropertyInfo[num_columns]; FieldInfo[] field_handlers = new FieldInfo[num_columns]; MethodInfo[] method_handlers = new MethodInfo[num_columns]; for (int i = 0; i < num_columns; i++) { prop_handlers[i] = return_type.GetProperty(first_line[i]); // If we failed to get a property handler, let's try a field handler if (prop_handlers[i] == null) { field_handlers[i] = return_type.GetField(first_line[i]); // If we failed to get a field handler, let's try a method if (field_handlers[i] == null) { // Methods must be treated differently - we have to ensure that the method has a single parameter MethodInfo mi = return_type.GetMethod(first_line[i]); if (mi != null) { if (mi.GetParameters().Length == 1) { method_handlers[i] = mi; column_types[i] = mi.GetParameters()[0].ParameterType; } else if (!ignore_bad_columns) { throw new Exception(String.Format("The column header '{0}' matched a method with more than one parameter.", first_line[i])); } // Does the caller want us to throw an error on bad columns? } else if (!ignore_bad_columns) { throw new Exception(String.Format("The column header '{0}' was not found in the class '{1}'.", first_line[i], return_type.FullName)); } } else { column_types[i] = field_handlers[i].FieldType; } } else { column_types[i] = prop_handlers[i].PropertyType; } // Retrieve a converter if (column_types[i] != null) { column_convert[i] = TypeDescriptor.GetConverter(column_types[i]); if (column_convert[i] == null) { throw new Exception(String.Format("The column {0} (type {1}) does not have a type converter.", first_line[i], column_types[i])); } } } // Alright, let's retrieve CSV lines and parse each one! int row_num = 1; foreach (string[] line in Lines()) { // Does this line match the length of the first line? Does the caller want us to complain? if (line.Length != num_columns) { if (!ignore_dimension_errors) { throw new Exception(String.Format("Line #{0} contains {1} columns; expected {2}", row_num, line.Length, num_columns)); } } // Construct a new object and execute each column on it T obj = new T(); for (int i = 0; i < Math.Min(line.Length, num_columns); i++) { // Attempt to convert this to the specified type object value = null; if (column_convert[i] != null && column_convert[i].IsValid(line[i])) { value = column_convert[i].ConvertFromString(line[i]); } else if (!ignore_type_conversion_errors) { throw new Exception(String.Format("The value '{0}' cannot be converted to the type {1}.", line[i], column_types[i])); } // Can we set this value to the object as a property? if (prop_handlers[i] != null) { prop_handlers[i].SetValue(obj, value, null); // Can we set this value to the object as a property? } else if (field_handlers[i] != null) { field_handlers[i].SetValue(obj, value); // Can we set this value to the object as a property? } else if (method_handlers[i] != null) { method_handlers[i].Invoke(obj, new object[] { value }); } } // Keep track of where we are in the file result.Add(obj); row_num++; } // Here's your array! return(result); }
/// <summary> /// Iterate through all lines in this CSV file /// </summary> /// <returns>An array of all data columns in the line</returns> public IEnumerable <string[]> Lines() { return(CSV.ParseStream(_stream, _settings)); }
/// <summary> /// Retrieve the next line from the file. /// </summary> /// <returns>One line from the file.</returns> public string[] NextLine() { return(CSV.ParseMultiLine(_instream, _delimiter, _text_qualifier)); }
/// <summary> /// Parse a new chunk of text retrieved via some other means than a stream. /// /// Call this function when you are retrieving your own text and when each chunk may or may not /// include line separators, and your stream does not consume line separators on its own. /// </summary> /// <param name="chunk">The new data to process</param> /// <param name="reachedEnd">Set this value to true </param> /// <returns>If this parsing operation produces a valid row, this will be non-null</returns> public string[] ParseChunk(string chunk, bool reachedEnd) { // Detect end of stream if (reachedEnd && string.IsNullOrEmpty(chunk) && _position == -1) { State = CSVState.Done; return(null); } // Add this chunk to the current processing logic _line += chunk; // Check for the presence of a "sep=" line once at the beginning of a stream if (_allowSepLine) { var newDelimiter = CSV.ParseSepLine(_line); _allowSepLine = false; if (newDelimiter != null) { _delimiter = newDelimiter.Value; return(null); } } // Process one character at a time from the current line while (_position < _line.Length || !reachedEnd) { _position++; // Have we reached the end of the stream? if (_position >= _line.Length) { if (reachedEnd) { // If we reached the end while still in a text qualifier, the CSV is broken if (_inTextQualifier) { State = CSVState.MissingTrailingQualifier; return(null); } // We always add the final work item here because trailing empty strings are valid State = CSVState.Done; _list.Add(_work.ToString()); _line = string.Empty; _position = -1; return(_list.ToArray()); } return(null); } var c = _line[_position]; // Are we currently processing a text block (which may optionally span multiple lines)? if (_inTextQualifier || (!_inTextQualifier && c == _settings.TextQualifier && _work.Length == 0)) { if (_inTextQualifier) { _work.Append(c); } _inTextQualifier = true; // Our next task is to find the end of this qualified-text field var p2 = -1; while (p2 < 0) { // If we don't see an end in sight, read more from the stream p2 = _line.IndexOf(_settings.TextQualifier, _position + 1); if (p2 < 0) { // No text qualifiers yet? Let's read more from the stream and continue _work.Append(_line.Substring(_position + 1)); _line = string.Empty; _position = -1; if (reachedEnd) { State = CSVState.MissingTrailingQualifier; } return(null); } // Append the text between the qualifiers _work.Append(_line.Substring(_position + 1, p2 - _position - 1)); _position = p2; // If the user put in a doubled-up qualifier, e.g. `""`, insert a single one and continue if (p2 + 1 < _line.Length && _line[p2 + 1] == _settings.TextQualifier) { _work.Append(_settings.TextQualifier); _position++; p2 = -1; } } // We're done parsing this text qualifier _inTextQualifier = false; } // Are we at a line separator? Let's do a quick test first else if (c == _settings.LineSeparator[0] && _position + _settings.LineSeparator.Length <= _line.Length) { if (string.Equals(_line.Substring(_position, _settings.LineSeparator.Length), _settings.LineSeparator)) { _line = _line.Substring(_position + _settings.LineSeparator.Length); _position = -1; _list.Add(_work.ToString()); var row = _list.ToArray(); _list.Clear(); _work.Length = 0; return(row); } } // Does this start a new field? else if (c == _delimiter) { // Is this a null token, and do we permit null tokens? var s = _work.ToString(); if (_settings.AllowNull && string.Equals(s, _settings.NullToken, StringComparison.Ordinal)) { _list.Add(null); } else { _list.Add(s); } _work.Length = 0; // Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space // Checks if the second parameter of the if statement will pass through successfully // e.g. `"bob", "mary", "bill"` if (_position + 2 <= _line.Length - 1) { if (_line[_position + 1].Equals(' ') && _line[_position + 2].Equals(_settings.TextQualifier)) { _position++; } } } // Regular character else { _work.Append(c); } } State = CSVState.Done; return(null); }
/// <summary> /// Read this file into a data table in memory /// </summary> /// <returns></returns> public DataTable ReadAsDataTable() { var dt = new DataTable(); string[] firstLine = null; // File contains column names - so name each column properly if (Headers == null) { var rawLine = _stream.ReadLine(); firstLine = CSV.ParseLine(rawLine, _settings); var list = new List <string>(); for (var i = 0; i < firstLine.Length; i++) { list.Add($"Column{i}"); } this.Headers = list.ToArray(); } // Add headers var numColumns = Headers.Length; foreach (var t in Headers) { dt.Columns.Add(new DataColumn(t, typeof(string))); } // If we had to read the first line to get dimensions, add it var row_num = 1; if (firstLine != null) { dt.Rows.Add(firstLine); row_num++; } // Start reading through the file foreach (var line in CSV.ParseStream(_stream, _settings)) { // Does this line match the length of the first line? if (line.Length != numColumns) { if (!_settings.IgnoreDimensionErrors) { throw new Exception($"Line #{row_num} contains {line.Length} columns; expected {numColumns}"); } else { // Add as best we can - construct a new line and make it fit var list = new List <string>(); list.AddRange(line); while (list.Count < numColumns) { list.Add(""); } dt.Rows.Add(list.GetRange(0, numColumns).ToArray()); } } else { dt.Rows.Add(line); } // Keep track of where we are in the file row_num++; } // Here's your data table return(dt); }
/// <summary> /// Iterate through all lines in this CSV file using async /// </summary> /// <returns>An array of all data columns in the line</returns> public IAsyncEnumerator <string[]> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken()) { return(CSV.ParseStreamAsync(_stream, _settings).GetAsyncEnumerator(cancellationToken)); }
/// <summary> /// Iterate through all lines in this CSV file using async /// </summary> /// <returns>An array of all data columns in the line</returns> public IAsyncEnumerable <string[]> LinesAsync() { return(CSV.ParseStreamAsync(_stream, _settings)); }
/// <summary> /// Iterate through all lines in this CSV file /// </summary> /// <returns></returns> public IEnumerator <string[]> GetEnumerator() { return(CSV.ParseStream(_stream, _settings).GetEnumerator()); }
/// <summary> /// Retrieve the next line from the file. /// </summary> /// <returns>One line from the file.</returns> public string[] NextLine() { return(CSV.ParseMultiLine(_instream, _settings)); }
/// <summary> /// Write one line to the file /// </summary> /// <param name="line">The array of values for this line</param> /// <param name="force_qualifiers">True if you want to force qualifiers for this line.</param> public void WriteLine(IEnumerable <object> line, bool force_qualifiers = false) { _outstream.WriteLine(CSV.Output(line, _delimiter, _text_qualifier, force_qualifiers)); }
/// <summary> /// Read this file into a data table in memory /// </summary> /// <param name="first_row_are_headers"></param> /// <returns></returns> public DataTable ReadAsDataTable(bool first_row_are_headers, bool ignore_dimension_errors, string[] headers = null) { DataTable dt = new DataTable(); // Read in the first line string[] first_line = CSV.ParseMultiLine(_instream, _delimiter, _text_qualifier); int num_columns = first_line.Length; // File contains column names - so name each column properly if (first_row_are_headers) { foreach (string header in first_line) { dt.Columns.Add(new DataColumn(header, typeof(string))); } // Okay, just create some untitled columns } else { if (headers == null) { for (int i = 0; i < num_columns; i++) { dt.Columns.Add(new DataColumn(String.Format("Column{0}", i), typeof(string))); } } else { for (int i = 0; i < headers.Length; i++) { dt.Columns.Add(new DataColumn(headers[i], typeof(string))); } } // Add this first line dt.Rows.Add(first_line); } // Start reading through the file int row_num = 1; foreach (string[] line in Lines()) { // Does this line match the length of the first line? if (line.Length != num_columns) { if (!ignore_dimension_errors) { throw new Exception(String.Format("Line #{0} contains {1} columns; expected {2}", row_num, line.Length, num_columns)); } else { // Add as best we can - construct a new line and make it fit List <string> list = new List <string>(); list.AddRange(line); while (list.Count < num_columns) { list.Add(""); } dt.Rows.Add(list.GetRange(0, num_columns).ToArray()); } } else { dt.Rows.Add(line); } // Keep track of where we are in the file row_num++; } // Here's your data table return(dt); }