/// <summary>Initializes a new instance of the <see cref="CsvWriter" /> class.</summary> /// <param name="properties">Extended properties.</param> /// <param name="layout">The table layout.</param> /// <param name="stream">The stream.</param> /// <param name="closeBaseStream">if set to <c>true</c> [close base stream on close].</param> public CsvWriter(RowLayout layout, Stream stream, CsvProperties properties = default, bool closeBaseStream = false) { BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); Layout = layout ?? throw new ArgumentNullException(nameof(layout)); Properties = properties.Valid ? properties : CsvProperties.Default; CloseBaseStream = closeBaseStream; writer = new DataWriter(stream, Properties.Encoding, Properties.NewLineMode); if (Properties.NoHeader) { return; } // write header for (var i = 0; i < Layout.FieldCount; i++) { if (i > 0) { writer.Write(Properties.Separator); } if (Properties.StringMarker.HasValue) { writer.Write(Properties.StringMarker.Value); } writer.Write(Layout[i].NameAtDatabase); if (Properties.StringMarker.HasValue) { writer.Write(Properties.StringMarker.Value); } } writer.WriteLine(); writer.Flush(); }
/// <summary> /// Reads a whole list from the specified csv file. /// </summary> /// <param name="properties">Properties of the csv file.</param> /// <param name="fileName">File name of the csv file.</param> /// <returns>Returns a new <see cref="List{TStruct}"/>.</returns> /// <typeparam name="TStruct">Structure type.</typeparam> public static IList <TStruct> ReadList <TStruct>(CsvProperties properties, string fileName) where TStruct : struct { var layout = RowLayout.CreateTyped(typeof(TStruct)); using var reader = new CsvReader(layout, fileName, properties); return(reader.ReadList <TStruct>()); }
/// <summary>Reads a whole list from the specified csv stream.</summary> /// <param name="properties">Properties of the csv file.</param> /// <param name="stream">The stream.</param> /// <returns>Returns a new <see cref="List{TStruct}" />.</returns> /// <typeparam name="TStruct">Structure type.</typeparam> public static List <TStruct> ReadList <TStruct>(CsvProperties properties, Stream stream) where TStruct : struct { var layout = RowLayout.CreateTyped(typeof(TStruct)); using (var reader = new CsvReader(layout, stream, properties)) { return(reader.ReadList <TStruct>()); } }
/// <summary>Creates a new csv file with the specified name and writes the whole table.</summary> /// <param name="table">Table to write to the csv file.</param> /// <param name="stream">The stream to write to.</param> /// <param name="properties">Properties of the csv file.</param> public static void WriteTable(ITable table, Stream stream, CsvProperties properties = default) { if (table == null) { throw new ArgumentNullException(nameof(table)); } var writer = new CsvWriter(table.Layout, stream, properties); try { writer.Write(table); } finally { writer.Close(); } }
/// <summary>Parses a single row of data from the specified string.</summary> /// <param name="properties">The csv properties.</param> /// <param name="layout">The row layout.</param> /// <param name="data">The buffer to parse.</param> /// <returns>Returns a new <see cref="Row" /> instance.</returns> public static Row ParseRow(CsvProperties properties, RowLayout layout, string data) { try { var fieldCount = layout.FieldCount; var fieldNumber = 0; var ident = new Queue <char>(); var identInARowCount = 0; var currentValue = new List <char>(); var i = -1; var values = new object[fieldCount]; while (fieldNumber < fieldCount) { ++i; if ((i == data.Length) && (fieldNumber == (fieldCount - 1))) { break; } if (i >= data.Length) { throw new InvalidDataException("Unexpected end of input!"); } if (properties.Separator == data[i]) { if (ident.Count == 0) { identInARowCount = 0; if (properties.StringMarker.HasValue) { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), properties.StringMarker.Value.ToString(), properties.Format); } else { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), string.Empty, properties.Format); } fieldNumber++; currentValue.Clear(); continue; } } if (properties.StringMarker == data[i]) { identInARowCount++; if ((ident.Count > 0) && (ident.Peek() == data[i])) { ident.Dequeue(); if (identInARowCount > 1) { // escaped char currentValue.Add(data[i]); } } else { ident.Enqueue(data[i]); } } else { identInARowCount = 0; currentValue.Add(data[i]); } } if (ident.Count > 0) { throw new InvalidDataException("Invalid ident/escaping!"); } if (properties.StringMarker.HasValue) { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), properties.StringMarker.Value.ToString(), properties.Format); } else { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), string.Empty, properties.Format); } fieldNumber++; if (i < data.Length) { if (properties.Separator == data[i]) { i++; } if (i < data.Length) { throw new InvalidDataException("Additional data at end of line!"); } } return(new Row(layout, values, false)); } catch (EndOfStreamException) { if (data.Length > 0) { throw; } return(null); } catch (Exception ex) { throw new InvalidDataException("Error while reading row data!", ex); } }
/// <summary>Initializes a new instance of the <see cref="CsvReader" /> class.</summary> /// <param name="properties">Properties to apply to the reader.</param> /// <param name="layout">Layout to use when reading the csv data.</param> /// <param name="stream">Stream to read data from.</param> /// <param name="closeBaseStream">if set to <c>true</c> [close base stream on close].</param> public CsvReader(RowLayout layout, Stream stream, CsvProperties properties = default, bool closeBaseStream = false) { Layout = layout; BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); Properties = properties.Valid ? properties : CsvProperties.Default; CloseBaseStream = closeBaseStream; reader = new DataReader(stream, Properties.Encoding, Properties.NewLineMode); if (!Properties.NoHeader) { var header = reader.ReadLine(); currentRowNumber++; var fields = header.Split(Properties.Separator); if (!Properties.AllowFieldMatching) { if (fields.Length != Layout.FieldCount) { if ((fields.Length - 1) != Layout.FieldCount) { throw new InvalidDataException($"Invalid header fieldcount (expected '{Layout.FieldCount}' got '{fields.Length}')!"); } } } else { if (fields.Length != Layout.FieldCount) { fieldNumberMatching = new int[Layout.FieldCount]; } } var count = Math.Min(Layout.FieldCount, fields.Length); for (var i = 0; i < count; i++) { var fieldName = fields[i].UnboxText(false); var fieldIndex = Layout.GetFieldIndex(fieldName, false); if (fieldIndex < 0) { throw new InvalidDataException( $"Error loading CSV Header! Got field name '{fieldName}' instead of '{Layout[i].Name}' at type '{Layout.Name}'"); } if (!Properties.AllowFieldMatching) { if (fieldIndex != i) { throw new InvalidDataException($"Fieldposition of Field '{fieldName}' does not match!"); } if (!string.Equals(Layout[fieldIndex].Name, fieldName)) { throw new InvalidDataException( $"Invalid header value at field number '{i}' name '{fieldName}' expected '{Layout[fieldIndex].Name}'!"); } } else { if ((fieldNumberMatching == null) && (fieldIndex != i)) { fieldNumberMatching = new int[Layout.FieldCount]; } } } if (fieldNumberMatching != null) { var i = 0; for (; i < count; i++) { var fieldName = fields[i].UnboxText(false); fieldNumberMatching[i] = Layout.GetFieldIndex(fieldName, false); } for (; i < Layout.FieldCount; i++) { fieldNumberMatching[i] = -1; } } } }
/// <summary>Initializes a new instance of the <see cref="CsvReader" /> class.</summary> /// <param name="properties">Properties to apply to the reader.</param> /// <param name="layout">Layout to use when reading from the csv file.</param> /// <param name="fileName">Filename to write to.</param> public CsvReader(RowLayout layout, string fileName, CsvProperties properties = default) : this(layout, File.OpenRead(fileName), properties, true) { }
/// <summary>Initializes a new instance of the <see cref="CsvWriter" /> class.</summary> /// <param name="layout">The table layout.</param> /// <param name="fileName">Filename to write to.</param> /// <param name="properties">Extended properties.</param> public CsvWriter(RowLayout layout, string fileName, CsvProperties properties = default) : this(layout, File.Create(fileName), properties, true) { }
/// <summary>Creates a new csv file with the specified name and writes the whole table.</summary> /// <param name="rows">Table to write to the csv file.</param> /// <param name="fileName">File name of the csv file.</param> /// <param name="properties">Properties of the csv file.</param> /// <typeparam name="TStruct">Structure type.</typeparam> public static void WriteAlien <TStruct>(IEnumerable <TStruct> rows, string fileName, CsvProperties properties = default) where TStruct : struct { var layout = RowLayout.CreateAlien(typeof(TStruct), false); var writer = new CsvWriter(layout, fileName, properties); try { writer.Write(rows); } finally { writer.Close(); } }
/// <summary>Creates a string representation of the specified row.</summary> /// <param name="properties">The csv properties.</param> /// <param name="layout">The row layout.</param> /// <param name="row">The row.</param> /// <returns>Returns a new string representing the row.</returns> public static string RowToString(CsvProperties properties, RowLayout layout, Row row) { var result = new StringBuilder(); var values = row.Values; for (var i = 0; i < layout.FieldCount; i++) { if (i > 0) { result.Append(properties.Separator); } if ((values != null) && (values[i] != null)) { var field = layout[i]; switch (field.DataType) { case DataType.Binary: { var str = Base64.NoPadding.Encode((byte[])values[i]); result.Append(str); break; } case DataType.Bool: case DataType.Int8: case DataType.Int16: case DataType.Int32: case DataType.Int64: case DataType.UInt8: case DataType.UInt16: case DataType.UInt32: case DataType.UInt64: { if (!properties.SaveDefaultValues && values[i].Equals(0)) { break; } var str = values[i].ToString(); result.Append(str); break; } case DataType.Char: { if (!properties.SaveDefaultValues && values[i].Equals((char)0)) { break; } var str = values[i].ToString(); result.Append(str); break; } case DataType.Decimal: { if (!properties.SaveDefaultValues && values[i].Equals(0m)) { break; } var value = (decimal)values[i]; result.Append(value.ToString(properties.Format)); break; } case DataType.Single: { if (!properties.SaveDefaultValues && values[i].Equals(0f)) { break; } var value = (float)values[i]; result.Append(value.ToString("R", properties.Format)); break; } case DataType.Double: { if (!properties.SaveDefaultValues && values[i].Equals(0d)) { break; } var value = (double)values[i]; result.Append(value.ToString("R", properties.Format)); break; } case DataType.TimeSpan: { if (!properties.SaveDefaultValues && values[i].Equals(TimeSpan.Zero)) { break; } var str = field.GetString(values[i], $"{properties.StringMarker}", properties.Format); result.Append(str); break; } case DataType.DateTime: { if (!properties.SaveDefaultValues && values[i].Equals(new DateTime(0))) { break; } string str; if (properties.DateTimeFormat != null) { str = ((DateTime)values[i]).ToString(properties.DateTimeFormat, properties.Format); } else { str = field.GetString(values[i], $"{properties.StringMarker}", properties.Format); } result.Append(str); break; } case DataType.User: case DataType.String: { if (!properties.SaveDefaultValues && values[i].Equals(string.Empty)) { break; } var str = values[i] == null ? string.Empty : values[i].ToString(); str = str.EscapeUtf8(); if (properties.StringMarker.HasValue) { str = str.Replace($"{properties.StringMarker}", $"{properties.StringMarker}{properties.StringMarker}"); result.Append(properties.StringMarker); } if (str.Length == 0) { result.Append(" "); } else { if (properties.StringMarker.HasValue) { if (str.StartsWith(properties.StringMarker.ToString())) { result.Append(" "); } } result.Append(str); if (properties.StringMarker.HasValue) { if (str.EndsWith(properties.StringMarker.ToString())) { result.Append(" "); } } } if (properties.StringMarker.HasValue) { result.Append(properties.StringMarker); } break; } case DataType.Enum: { if (!properties.SaveDefaultValues && Convert.ToInt32(values[i]).Equals(0)) { break; } var str = values[i].ToString(); result.Append(str); break; } default: throw new NotImplementedException($"DataType {layout[i].DataType} is not implemented!"); } } } return(result.ToString()); }