// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Data; using System.IO; namespace Spludlow.Data { public static class TextTable { public static void WriteDirectory(string directoryName, DataSet dataSet) { WriteDirectory(directoryName, dataSet, Encoding.Default); } public static void WriteDirectory(string directoryName, DataSet dataSet, Encoding encoding) { if (Directory.Exists(directoryName) == true) Directory.Delete(directoryName, true); Directory.CreateDirectory(directoryName); foreach (DataTable table in dataSet.Tables) { string filename = directoryName + @"\" + table.TableName + ".txt"; Write(filename, table, encoding); } } public static DataSet ReadDirectory(string directoryName) { return ReadDirectory(directoryName, Encoding.Default); } public static DataSet ReadDirectory(string directoryName, Encoding encoding) { string dataSetName = Path.GetFileName(directoryName); DataSet dataSet = new DataSet(dataSetName); foreach (string filename in Directory.GetFiles(directoryName, "*.txt")) { DataTable table = ReadFile(filename, encoding); table.TableName = Path.GetFileNameWithoutExtension(filename); dataSet.Tables.Add(table); } return dataSet; } public static void Write(string filename, DataTable table) { Write(filename, new DataView(table), new string[0], Encoding.Default); } public static void Write(string filename, DataTable table, Encoding encoding) { Write(filename, new DataView(table), new string[0], encoding); } public static void Write(string filename, DataView view) { Write(filename, view, new string[0], Encoding.Default); } public static void Write(string filename, DataView view, Encoding encoding) { Write(filename, view, new string[0], encoding); } public static void Write(string filename, DataTable table, string[] columnNames) { Write(filename, new DataView(table), columnNames, Encoding.Default); } public static void Write(string filename, DataTable table, string[] columnNames, Encoding encoding) { Write(filename, new DataView(table), columnNames, encoding); } public static void Write(string filename, DataView view, string[] columnNames) { Write(filename, view, columnNames, Encoding.Default); } public static void Write(string filename, DataView view, string[] columnNames, Encoding encoding) { using (FileStream stream = Spludlow.Io.Files.FileStreamCreateWrite(filename)) Write(stream, view, columnNames, encoding); } public static string Write(DataTable table) { return Write(new DataView(table), null); } public static string Write(DataView view, string[] columnNames) { using (MemoryStream stream = new MemoryStream()) { Write(stream, view, columnNames); stream.Seek(0, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(stream)) return reader.ReadToEnd(); } } public static void Write(Stream stream, DataTable table, Encoding encoding) { Write(stream, new DataView(table), new string[0], encoding); } public static void Write(Stream stream, DataView view, string[] columnNames) { Write(stream, view, columnNames, Encoding.Default); } public static void Write(Stream stream, DataView view, string[] columnNames, Encoding encoding) { Write(stream, view, columnNames, encoding, false); } public static void Write(Stream stream, DataTable table, Encoding encoding, bool appendMode) { Write(stream, new DataView(table), new string[0], encoding, appendMode); } public static void Write(Stream stream, DataView view, string[] columnNames, Encoding encoding, bool appendMode) { DataTable table = view.Table; List columnIndexes = new List(); if (columnNames == null || columnNames.Length == 0) { for (int index = 0; index < table.Columns.Count; ++index) columnIndexes.Add(index); } else { foreach (string columnName in columnNames) columnIndexes.Add(table.Columns[columnName].Ordinal); } List keyColumns = new List(); foreach (DataColumn column in table.PrimaryKey) keyColumns.Add(column.ColumnName); using (StreamWriter writer = new StreamWriter(stream, encoding, 4096, true)) { bool first; if (appendMode == false) { first = true; foreach (int index in columnIndexes) { if (first == true) first = false; else writer.Write('\t'); string columnName = table.Columns[index].ColumnName; writer.Write(columnName); } writer.WriteLine(); first = true; foreach (int index in columnIndexes) { if (first == true) first = false; else writer.Write('\t'); writer.Write(table.Columns[index].DataType.Name); string columnName = table.Columns[index].ColumnName; if (keyColumns.Contains(columnName) == true) writer.Write("*"); } writer.WriteLine(); } foreach (DataRowView rowView in view) { DataRow row = rowView.Row; first = true; foreach (int index in columnIndexes) { if (first == true) first = false; else writer.Write('\t'); if (row.IsNull(index) == false) { string typeName = table.Columns[index].DataType.Name; if (Spludlow.SimpleEncoding.IsSimple(typeName) == true) writer.Write(Spludlow.SimpleEncoding.Encode(row[index], typeName)); else writer.Write("@" + typeName); } } writer.WriteLine(); } writer.Flush(); } } public static DataTable ReadText(string tableName, string[] lines) { DataTable table = ReadText(lines); table.TableName = tableName; return table; } public static DataTable ReadText(string[] lines) { return ReadText(lines, true); } public static DataTable ReadText(string[] lines, bool removeEmptyEntries) { StringBuilder text = new StringBuilder(); foreach (string line in lines) text.AppendLine(line); return ReadText(text.ToString(), removeEmptyEntries); } public static DataTable ReadText(string text) { return ReadText(text, false); } public static DataTable ReadText(string text, bool removeEmptyEntries) { byte[] data = Encoding.Default.GetBytes(text); using (MemoryStream stream = new MemoryStream(data)) return ReadStream(stream, removeEmptyEntries); } public static DataTable ReadFile(string filename) // No table name set --- use Table1 or somthing { return ReadFile(filename, false, Encoding.Default); } public static DataTable ReadFile(string filename, Encoding encoding) { return ReadFile(filename, false, encoding); } public static DataSet ReadFileRemote(string host, string filename, bool removeEmptyEntries) { return (DataSet)Spludlow.Call.Now(host, "Spludlow", "Spludlow.Data.TextTable", "ReadFileRemoteWork", new object[] { filename, removeEmptyEntries }); } public static DataSet ReadFileRemoteWork(string filename, bool removeEmptyEntries) { DataSet dataSet = new DataSet(); DataTable table = ReadFile(filename, removeEmptyEntries); dataSet.Tables.Add(table); return dataSet; } public static DataTable ReadFile(string filename, bool removeEmptyEntries) { return ReadFile(filename, removeEmptyEntries, Encoding.Default); } public static DataTable ReadFile(string filename, bool removeEmptyEntries, Encoding encoding) { using (FileStream stream = Spludlow.Io.Files.FileStreamOpenRead(filename)) return ReadStream(stream, removeEmptyEntries, encoding); } public static DataTable ReadStream(Stream stream, bool removeEmptyEntries) { return ReadStream(stream, removeEmptyEntries, Encoding.Default); } public static DataTable ReadStream(Stream stream, bool removeEmptyEntries, Encoding encoding) { return ReadStream(stream, removeEmptyEntries, encoding, false); } public static DataTable ReadStream(Stream stream, bool removeEmptyEntries, Encoding encoding, bool schemaOnly) { StringSplitOptions options = StringSplitOptions.None; if (removeEmptyEntries == true) options = StringSplitOptions.RemoveEmptyEntries; using (StreamReader reader = new StreamReader(stream, encoding, true, 4096, true)) { string line; string[] columnNames = null; string[] columnTypes = null; List keyColumnIndexes = new List(); for (int lineNumber = 0; lineNumber < 2; ++lineNumber) { line = reader.ReadLine(); string[] words = line.Split(new char[] { '\t' }, options); if (lineNumber == 0) { columnNames = words; } else { for (int index = 0; index < words.Length; ++index) { if (words[index].EndsWith("*") == true) { words[index] = words[index].Substring(0, words[index].Length - 1); keyColumnIndexes.Add(index); } } columnTypes = words; } } if (columnNames == null || columnTypes == null || (columnNames.Length != columnTypes.Length)) throw new ApplicationException("Bad Table."); DataTable table = new DataTable(); for (int index = 0; index < columnNames.Length; ++index) { string name = columnNames[index]; string typeName = columnTypes[index]; if (typeName.Contains(".") == false) typeName = "System." + typeName; Type type = Type.GetType(typeName, true); table.Columns.Add(name, type); } if (keyColumnIndexes.Count > 0) { List list = new List(); foreach (int index in keyColumnIndexes) list.Add(table.Columns[index]); table.PrimaryKey = list.ToArray(); } if (schemaOnly == true) return table; while ((line = reader.ReadLine()) != null) { if (removeEmptyEntries == true) { line = line.Trim(); if (line.Length == 0 || line.StartsWith("#")) continue; } DataRow row = table.NewRow(); string[] words = line.Split(new char[] { '\t' }, options); if (words.Length > columnNames.Length) throw new ApplicationException("Bad Table Row, more columns in line than table."); for (int index = 0; index < words.Length; ++index) { object data; if (columnTypes[index] == "String") { string text = (string)words[index]; if (text == "") { data = null; } else { if (text == "\"\"") data = ""; else data = text; } } else { data = Spludlow.SimpleEncoding.Decode(words[index], columnTypes[index]); } if (data != null) row[index] = data; } table.Rows.Add(row); } return table; } } public static DataSet ReadDirectory(string directory, bool removeEmptyEntries) // should be table naem array !!!! { DataSet dataSet = new DataSet(); foreach (string filename in Directory.GetFiles(directory, "*.txt")) { string name = Path.GetFileNameWithoutExtension(filename); bool useRemove = removeEmptyEntries; DataTable table = ReadFile(filename, useRemove); table.TableName = name; dataSet.Tables.Add(table); } return dataSet; } public static void CleanWhiteSpace(DataTable table) { Regex cleanWhite = new Regex(@"\s+"); foreach (DataColumn column in table.Columns) { if (column.DataType == typeof(string) || column.DataType == typeof(char)) { bool isString = (column.DataType == typeof(string)); foreach (DataRow row in table.Rows) { if (row.IsNull(column) == true) continue; if (isString == true) { string text = (string)row[column]; if (cleanWhite.IsMatch(text) == false) continue; text = cleanWhite.Replace(text, " "); if (text.Length > 0) // .Trim(); { if (text[0] == ' ') text = text.Substring(1); if (text.Length > 0 && text[text.Length - 1] == ' ') text = text.Substring(0, text.Length - 1); } row[column] = text; } else { char ch = (char)row[column]; if (Char.IsWhiteSpace(ch) == true) row[column] = ' '; } } } } } public static void CombineTables(string[] sourceTableFilenames, string targetTableFilename) { CombineTables(sourceTableFilenames, targetTableFilename, Encoding.Default); } public static void CombineTables(string[] sourceTableFilenames, string targetTableFilename, Encoding encoding) { if (File.Exists(targetTableFilename) == true) File.Delete(targetTableFilename); for (int sourceIndex = 0; sourceIndex < sourceTableFilenames.Length; ++sourceIndex) { if (sourceIndex == 0) { File.Copy(sourceTableFilenames[0], targetTableFilename); } else { using (StreamWriter writer = new StreamWriter(targetTableFilename, true, encoding)) { writer.BaseStream.Seek(0, SeekOrigin.End); using (StreamReader reader = new StreamReader(sourceTableFilenames[sourceIndex], encoding)) { string line; for (int lineIndex = 0; (line = reader.ReadLine()) != null; ++lineIndex) { if (lineIndex < 2 || line.Length == 0) continue; writer.WriteLine(line); } } } } } } public static void TestTextTableSafty(string filename, DataTable table, int threads, int iterations) { Spludlow.Log.Report("TestTextTableSafty Starting"); if (File.Exists(filename) == true) File.Delete(filename); Write(filename, table); Task[] tasks = new Task[threads]; for (int index = 0; index < threads; ++index) { int threadId = index; tasks[index] = new Task(() => TestTextTableSaftyWorker(filename, iterations, threadId)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); } private static void TestTextTableSaftyWorker(string filename, int iterations, int threadId) { Random rand = new Random(); for (int pass = 0; pass < iterations; ++pass) { DataTable table = ReadFile(filename); System.Threading.Thread.Sleep(rand.Next(500)); Write(filename, table); Spludlow.Log.Info("TestTextTableSafty; Thread: " + threadId + ", Pass: " + pass); } } } }