Beispiel #1
0
        /// <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));
        }
Beispiel #2
0
        /// <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));
        }
Beispiel #3
0
        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));
            }
        }
Beispiel #4
0
        /// <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);
            }
        }
Beispiel #5
0
        /// <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);
        }
Beispiel #6
0
 /// <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);
 }
Beispiel #7
0
        /// <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);
        }
Beispiel #8
0
 /// <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));
 }
Beispiel #9
0
 /// <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));
 }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        /// <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);
        }
Beispiel #12
0
 /// <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));
 }
Beispiel #13
0
 /// <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));
 }
Beispiel #14
0
 /// <summary>
 /// Iterate through all lines in this CSV file
 /// </summary>
 /// <returns></returns>
 public IEnumerator <string[]> GetEnumerator()
 {
     return(CSV.ParseStream(_stream, _settings).GetEnumerator());
 }
Beispiel #15
0
 /// <summary>
 /// Retrieve the next line from the file.
 /// </summary>
 /// <returns>One line from the file.</returns>
 public string[] NextLine()
 {
     return(CSV.ParseMultiLine(_instream, _settings));
 }
Beispiel #16
0
 /// <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));
 }
Beispiel #17
0
        /// <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);
        }