using System; using System.ComponentModel; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.Common; using System.IO; using System.Net; using FastReport.Utils; using System.Globalization; using System.Collections; namespace FastReport.Data { internal static class CsvUtils { /// /// The default field name. /// public const string DEFAULT_FIELD_NAME = "Field"; private static void DetermineTypes(List lines, DataTable table, NumberFormatInfo numberInfo, NumberFormatInfo currencyInfo, DateTimeFormatInfo dateTimeInfo) { int intTemp; double doubleTemp; decimal decimalTemp; DateTime dateTemp; for (int i = 0; i < table.Columns.Count; i++) { // gather types here Dictionary types = new Dictionary(); // check all values in the column for (int j = 0; j < lines.Count; j++) { if (i >= lines[j].Length) { // number of values is less than number of table columns. Reasons: wrong separator or bad-formed csv file? // just skip this line } else { string value = lines[j][i]; if (!String.IsNullOrEmpty(value)) { if (Int32.TryParse(value, out intTemp)) { types[typeof(Int32)] = 1; } else if (value.Contains(currencyInfo.CurrencySymbol) && Decimal.TryParse(value, NumberStyles.Currency, currencyInfo, out decimalTemp)) { types[typeof(Decimal)] = 1; } else if (Double.TryParse(value, NumberStyles.Number, numberInfo, out doubleTemp)) { types[typeof(Double)] = 1; } else if (DateTime.TryParse(value, dateTimeInfo, DateTimeStyles.NoCurrentDateDefault, out dateTemp)) { types[typeof(DateTime)] = 1; } else { types[typeof(String)] = 1; break; } } } } // cases allowed: // - single type -> the type // - mix of ints and doubles -> double // - all others should not be mixed -> string Type guessType = typeof(String); if (types.Count == 1) { // get a single value this way foreach (Type t in types.Keys) { guessType = t; } } else if (types.Count == 2 && types.ContainsKey(typeof(Int32)) && types.ContainsKey(typeof(Double))) { guessType = typeof(Double); } table.Columns[i].DataType = guessType; } } internal static List ReadLines(CsvConnectionStringBuilder builder, int maxLines = 0) { if (String.IsNullOrEmpty(builder.CsvFile) || String.IsNullOrEmpty(builder.Separator)) return null; ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; WebRequest request; WebResponse response = null; try { // fix for datafile in current folder if (File.Exists(builder.CsvFile)) builder.CsvFile = Path.GetFullPath(builder.CsvFile); Uri uri = new Uri(builder.CsvFile); if (uri.IsFile) { if (Config.ForbidLocalData) throw new Exception(Res.Get("ConnectionEditors,Common,OnlyUrlException")); request = (FileWebRequest)WebRequest.Create(uri); request.Timeout = 5000; response = (FileWebResponse)request.GetResponse(); } else if (uri.OriginalString.StartsWith("http")) { request = (HttpWebRequest)WebRequest.Create(uri); request.Timeout = 5000; response = (HttpWebResponse)request.GetResponse(); } else if (uri.OriginalString.StartsWith("ftp")) { request = (FtpWebRequest)WebRequest.Create(uri); request.Timeout = 5000; response = (FtpWebResponse)request.GetResponse(); } } catch (Exception e) { throw e; } if (response == null) return null; List lines = new List(); if (maxLines == 0) maxLines = int.MaxValue; // read lines using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(builder.Codepage))) { for (int i = 0; i < maxLines; i++) { string line = reader.ReadLine(); // end of stream reached if (line == null) break; // skip empty lines if (!String.IsNullOrEmpty(line)) lines.Add(line); } } return lines; } internal static DataTable CreateDataTable(CsvConnectionStringBuilder builder, List rawLines) { if (rawLines == null) return null; // split each line to array of values List lines = new List(); for (int i = 0; i < rawLines.Count; i++) { string line = rawLines[i]; string[] values = line.Split(builder.Separator.ToCharArray()); if (builder.RemoveQuotationMarks) { for (int j = 0; j < values.Length; j++) { values[j] = values[j].Trim("\"".ToCharArray()); } } lines.Add(values); } if (lines.Count == 0) return null; NumberFormatInfo numberInfo = CultureInfo.GetCultureInfo(builder.NumberFormat)?.NumberFormat ?? CultureInfo.CurrentCulture.NumberFormat; NumberFormatInfo currencyInfo = CultureInfo.GetCultureInfo(builder.CurrencyFormat)?.NumberFormat ?? CultureInfo.CurrentCulture.NumberFormat; DateTimeFormatInfo dateTimeInfo = CultureInfo.GetCultureInfo(builder.DateTimeFormat)?.DateTimeFormat ?? CultureInfo.CurrentCulture.DateTimeFormat; // get table name from file name string tableName = Path.GetFileNameWithoutExtension(builder.CsvFile).Replace(".", "_"); if (String.IsNullOrEmpty(tableName)) { tableName = "Table"; } DataTable table = new DataTable(tableName); string[] fields = lines[0]; // create table columns for (int i = 0; i < fields.Length; i++) { DataColumn column = new DataColumn(); column.DataType = typeof(string); // get field names from first string if needed string fieldName = fields[i].Replace("\t", ""); if (builder.FieldNamesInFirstString && !table.Columns.Contains(fieldName)) { column.ColumnName = fieldName; column.Caption = column.ColumnName; } else { column.ColumnName = DEFAULT_FIELD_NAME + i.ToString(); column.Caption = column.ColumnName; } table.Columns.Add(column); } int startIndex = builder.FieldNamesInFirstString ? 1 : 0; // cast types of fields if needed if (builder.ConvertFieldTypes) { int number = lines.Count - startIndex; DetermineTypes(lines.GetRange(startIndex, number), table, numberInfo, currencyInfo, dateTimeInfo); } // add table rows for (int i = startIndex; i < lines.Count; i++) { if (lines[i].Length > 0) { // get values from the string fields = lines[i]; // add a new row DataRow row = table.NewRow(); int valuesCount = fields.Length < table.Columns.Count ? fields.Length : table.Columns.Count; for (int j = 0; j < valuesCount; j++) { string value = fields[j]; if (!String.IsNullOrEmpty(value)) { if (table.Columns[j].DataType == typeof(String)) { row[j] = value; } else if (table.Columns[j].DataType == typeof(Int32)) { row[j] = Int32.Parse(value); } else if (table.Columns[j].DataType == typeof(Decimal)) { row[j] = Decimal.Parse(value, NumberStyles.Currency, currencyInfo); } else if (table.Columns[j].DataType == typeof(Double)) { row[j] = Double.Parse(value, NumberStyles.Number, numberInfo); } else if (table.Columns[j].DataType == typeof(DateTime)) { row[j] = DateTime.Parse(value, dateTimeInfo); } } } table.Rows.Add(row); } } return table; } } }