/* This file is part of https://github.com/aiten/Framework. Copyright (c) Herbert Aitenbichler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ namespace Iot.Tools { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using System.Threading.Tasks; public class CsvImportBase { private readonly NumberFormatInfo _nfi; public Encoding Encoding { get; set; } = Encoding.Default; public string DateFormat { get; set; } = "yyyy/MM/dd"; public string TimeFormat { get; set; } = "HH:mm:ss"; public string Fraction3Format { get; set; } = ".fff"; public string Fraction5Format { get; set; } = ".fffff"; public string DateTimeFormat => GetDateTimeFormat(DateFormat, TimeFormat); public string DateTimeFraction1Format => GetDateTimeFormat(DateFormat, TimeFormat, Fraction3Format); public string DateTimeFraction5Format => GetDateTimeFormat(DateFormat, TimeFormat, Fraction5Format); public string GetDateTimeFormat(string dateFormat, string timeFormat, string fractionFormat = null) => $"{dateFormat} {timeFormat}{fractionFormat ?? ""}"; public event EventHandler> ReadFirstLine; public char ListSeparatorChar { get; set; } = ';'; public string NewLineInString { get; set; } = "\n"; public CsvImportBase() { // Retrieve a writable NumberFormatInfo object. var enUS = CultureInfo.CreateSpecificCulture("en-US"); _nfi = enUS.NumberFormat; } public NumberFormatInfo NumberFormat => _nfi; public void SetAustriaNumberFormat() { _nfi.NumberDecimalSeparator = ","; _nfi.NumberGroupSeparator = "."; } #region read public IList> ReadStringMatrixFromCsv(string[] lines, bool skipTitleLine) { var elements = new List>(); var lineIdx = 0; var readLineIdx = 0; var compareLineIdx = skipTitleLine ? 1 : 0; while (true) { var row = ReadLine(() => { if (readLineIdx >= lines.Length) { return null; } return lines[readLineIdx++]; }); if (row == null) { break; } if (lineIdx == 0) { ReadFirstLine?.Invoke(this, row); } if (lineIdx >= compareLineIdx) { elements.Add(row); } lineIdx++; } return elements; } public IList> ReadStringMatrixFromCsv(string fileName, bool skipTitleLine) { var lines = File.ReadAllLines(fileName, Encoding); return ReadStringMatrixFromCsv(lines, skipTitleLine); } public async Task>> ReadStringMatrixFromCsvAsync(string fileName, bool skipTitleLine) { var lines = await File.ReadAllLinesAsync(fileName, Encoding); return ReadStringMatrixFromCsv(lines, skipTitleLine); } private IList ReadLine(Func getNextLine) { var line = getNextLine(); if (line == null) { return null; } var columns = new List(); var sb = new StringBuilder(line.Length); var noQuoteChar = '\0'; var quoteChar = noQuoteChar; while (true) { for (var idx = 0; idx < line.Length; idx++) { var ch = line[idx]; if (ch == quoteChar) { // end of " or "" if (idx + 1 < line.Length && line[idx + 1] == quoteChar) { idx++; sb.Append(ch); } else { quoteChar = noQuoteChar; } } else if (quoteChar == noQuoteChar && ch == '"') { quoteChar = ch; } else if (quoteChar == noQuoteChar && ch == ListSeparatorChar) { columns.Add(sb.ToString()); sb.Clear(); } else if (quoteChar == noQuoteChar && ch == '|') { columns.Add(sb.ToString()); sb.Clear(); } else { sb.Append(ch); } } if (quoteChar == noQuoteChar) { break; } sb.Append(NewLineInString); line = getNextLine(); } columns.Add(sb.ToString()); return columns; } #endregion #region convert public string ExcelString(string excelField) { return excelField; } public short ExcelShort(string excelField) { return short.Parse(excelField); } public int ExcelInt(string excelField) { return int.Parse(excelField); } public long ExcelLong(string excelField) { return long.Parse(excelField); } public ushort ExcelUShort(string excelField) { return ushort.Parse(excelField); } public uint ExcelUInt(string excelField) { return uint.Parse(excelField); } public ulong ExcelULong(string excelField) { return ulong.Parse(excelField); } public float ExcelFloat(string excelField) { return float.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi); } public byte ExcelByte(string excelField) { return byte.Parse(excelField); } public bool ExcelBool(string excelField) { switch (excelField) { case @"0": case @"false": return false; case @"1": case @"true": return true; default: throw new ArgumentOutOfRangeException(nameof(excelField), excelField, $@"cannot convert '{excelField}' to 'bool'."); } } public decimal ExcelDecimal(string excelField) { return decimal.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi); } public double ExcelDouble(string excelField) { return double.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi); } public DateTime ExcelDateOrDateTime(string excelField) { if (excelField.Length > DateFormat.Length) { return ExcelDateTime(excelField, DateFormat, TimeFormat, Fraction3Format, Fraction3Format); } return ExcelDate(excelField, DateFormat); } public DateTime ExcelDate(string excelField, string format) { try { return DateTime.ParseExact(excelField, format, CultureInfo.InvariantCulture); } catch (Exception) { throw; } } public DateTime ExcelDate(string excelField) { return ExcelDate(excelField, DateFormat); } public DateTime ExcelDateDMY(string excelField) { // Parse date and time with custom specifier. // e.g. string dateString = "19.01.2018"; return ExcelDate(excelField, "dd.MM.yyyy"); } public DateTime ExcelDateYMD(string excelField) { // Parse date and time with custom specifier. // e.g. string dateString = "19.01.2018"; return ExcelDate(excelField, "yyyy/MM/dd"); } public object ExcelTimeSpan(string excelField) { try { var timeSpan = TimeSpan.Parse(excelField); return timeSpan; } catch (Exception e) { throw new Exception(e.StackTrace); } } public DateTime ExcelDateTime(string excelField, string formatDate, string formatTime, string formatFraction3, string formatFraction5) { try { var fractionFormat = string.Empty; var dotIdx = excelField.LastIndexOf('.'); if (dotIdx > formatDate.Length) { var fractionLength = excelField.Length - dotIdx - 1; fractionFormat = fractionLength == 3 ? formatFraction3 : formatFraction5; } return DateTime.ParseExact(excelField, GetDateTimeFormat(formatDate, formatTime, fractionFormat), CultureInfo.InvariantCulture); } catch (Exception) { throw; } } public DateTime ExcelDateTime(string excelField) { return ExcelDateTime(excelField, DateFormat, TimeFormat, Fraction3Format, Fraction5Format); } public DateTime ExcelDateTimeYMD(string excelField) { return ExcelDateTime(excelField, "yyyy/MM/dd", TimeFormat, Fraction3Format, Fraction5Format); } public object ExcelEnum(Type enumType, string excelField) { try { var enumValue = Enum.Parse(enumType, excelField); return enumValue; } catch (Exception) { throw; } } public byte[] ExcelImage(string excelField) { byte[] bytes; if (excelField.StartsWith(@"0x")) { if (excelField.Length % 2 == 1) { throw new ArgumentException(@"string has odd length.", nameof(excelField)); } int length = (excelField.Length - 2) / 2; int chIdx = 2; bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = (byte)(ToHex(excelField[chIdx]) * 16 + ToHex(excelField[chIdx + 1])); chIdx += 2; } } else { bytes = Convert.FromBase64String(excelField); } return bytes; } private int ToHex(char ch) { if (ch >= '0' && ch <= '9') { return ch - '0'; } if (ch >= 'a' && ch <= 'f') { return 10 + ch - 'a'; } if (ch >= 'A' && ch <= 'F') { return 10 + ch - 'F'; } throw new ArgumentException(nameof(ch), $@"'{ch}' is not a hex digit."); } #endregion } }