mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-07-13 20:30:48 +00:00
This change looks dramatic, but it's just separating out the already-split namespaces into separate top-level folders. In theory, every single one could be built into their own Nuget package. `SabreTools.IO.Meta` builds the normal Nuget package that is used by all other projects and includes all namespaces. `SabreTools.IO` builds to `SabreTools.IO.Common` to avoid overwriting issues on publish.
198 lines
5.4 KiB
C#
198 lines
5.4 KiB
C#
using System;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace SabreTools.Text.SeparatedValue
|
|
{
|
|
public class Writer : IDisposable
|
|
{
|
|
#region Fields
|
|
|
|
/// <summary>
|
|
/// Set if values should be wrapped in quotes
|
|
/// </summary>
|
|
public bool Quotes { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Set what character should be used as a separator
|
|
/// </summary>
|
|
public char Separator { get; set; } = ',';
|
|
|
|
/// <summary>
|
|
/// Set if field count should be verified from the first row
|
|
/// </summary>
|
|
public bool VerifyFieldCount { get; set; } = true;
|
|
|
|
#endregion
|
|
|
|
#region Private Properties
|
|
|
|
/// <summary>
|
|
/// Internal stream writer
|
|
/// </summary>
|
|
private readonly StreamWriter _writer;
|
|
|
|
/// <summary>
|
|
/// Internal value if we've written a header before
|
|
/// </summary>
|
|
private bool _header = false;
|
|
|
|
/// <summary>
|
|
/// Internal value if we've written our first line before
|
|
/// </summary>
|
|
private bool _firstRow = false;
|
|
|
|
/// <summary>
|
|
/// Internal value to say how many fields should be written
|
|
/// </summary>
|
|
private int _fields = -1;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Constructor for writing to a file
|
|
/// </summary>
|
|
public Writer(string filename)
|
|
{
|
|
_writer = new StreamWriter(filename);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Consturctor for writing to a stream
|
|
/// </summary>
|
|
public Writer(Stream stream, Encoding encoding)
|
|
{
|
|
#if NET20 || NET35 || NET40
|
|
_writer = new StreamWriter(stream, encoding);
|
|
#else
|
|
_writer = new StreamWriter(stream, encoding, 1024, leaveOpen: true);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Consturctor for writing to a stream writer
|
|
/// </summary>
|
|
public Writer(StreamWriter streamWriter)
|
|
{
|
|
_writer = streamWriter;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Write a header row
|
|
/// </summary>
|
|
public void WriteHeader(string?[] headers)
|
|
{
|
|
// If we haven't written anything out, we can write headers
|
|
if (!_header && !_firstRow)
|
|
WriteValues(headers);
|
|
|
|
_header = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a value row
|
|
/// </summary>
|
|
/// <param name="values">Array representing the values</param>
|
|
/// <param name="newline">True to append a newline, false otherwise</param>
|
|
/// <exception cref="ArgumentException">
|
|
/// Thrown if <see cref="Separator"/> is an invalid character.
|
|
/// </exception>
|
|
/// <exception cref="DataException">
|
|
/// Thrown if the underlying stream is not marked as writable.
|
|
/// </exception>
|
|
public void WriteValues(object?[] values, bool newline = true)
|
|
{
|
|
// If the writer can't be used, we error
|
|
if (!_writer.BaseStream.CanWrite)
|
|
throw new DataException(nameof(_writer));
|
|
|
|
// If the separator character is invalid, we error
|
|
if (Separator == default(char))
|
|
throw new ArgumentException(nameof(Separator));
|
|
|
|
// If we have the first row, set the bool and the field count
|
|
if (!_firstRow)
|
|
{
|
|
_firstRow = true;
|
|
if (VerifyFieldCount && _fields == -1)
|
|
_fields = values.Length;
|
|
}
|
|
|
|
// Get the number of fields to write out
|
|
int fieldCount = values.Length;
|
|
if (VerifyFieldCount)
|
|
fieldCount = Math.Min(fieldCount, _fields);
|
|
|
|
// Iterate over the fields, writing out each
|
|
bool firstField = true;
|
|
for (int i = 0; i < fieldCount; i++)
|
|
{
|
|
var value = values[i];
|
|
|
|
if (!firstField)
|
|
_writer.Write(Separator);
|
|
|
|
if (Quotes)
|
|
_writer.Write("\"");
|
|
_writer.Write(value?.ToString() ?? string.Empty);
|
|
if (Quotes)
|
|
_writer.Write("\"");
|
|
|
|
firstField = false;
|
|
}
|
|
|
|
// If we need to pad out the number of fields, add empties
|
|
if (VerifyFieldCount && values.Length < _fields)
|
|
{
|
|
for (int i = 0; i < _fields - values.Length; i++)
|
|
{
|
|
_writer.Write(Separator);
|
|
|
|
if (Quotes)
|
|
_writer.Write("\"\"");
|
|
}
|
|
}
|
|
|
|
// Add a newline, if needed
|
|
if (newline)
|
|
_writer.WriteLine();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a generic string
|
|
/// </summary>
|
|
public void WriteString(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return;
|
|
|
|
_writer.Write(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flush the underlying writer
|
|
/// </summary>
|
|
public void Flush()
|
|
{
|
|
_writer.Flush();
|
|
}
|
|
|
|
#region IDisposable Implementation
|
|
|
|
/// <summary>
|
|
/// Dispose of the underlying writer
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
_writer.Dispose();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|