Update
This commit is contained in:
54
ConsoleApp2/Helpers/Database/DatabaseHelper.cs
Normal file
54
ConsoleApp2/Helpers/Database/DatabaseHelper.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Data;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace ConsoleApp2.Helpers.Database;
|
||||
|
||||
public static class DatabaseHelper
|
||||
{
|
||||
public static async Task<DataSet> QueryTableAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
var ds = new DataSet();
|
||||
var adapter = new MySqlDataAdapter(cmd).Fill(ds);
|
||||
return ds;
|
||||
}
|
||||
|
||||
public static async Task<object?> QueryScalarAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
return await cmd.ExecuteScalarAsync();
|
||||
}
|
||||
|
||||
public static async Task<int> NonQueryAsync(string connStr, string sql)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
return await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
public static async Task<int> TransactionAsync(string connStr, string sql, params MySqlParameter[] parameters)
|
||||
{
|
||||
await using var conn = new MySqlConnection(connStr);
|
||||
if(conn.State is not ConnectionState.Open)
|
||||
await conn.OpenAsync();
|
||||
await using var trans = await conn.BeginTransactionAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = trans;
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
var rows = await cmd.ExecuteNonQueryAsync();
|
||||
await trans.CommitAsync();
|
||||
return rows;
|
||||
}
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
using ConsoleApp2.Options;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using ZstdSharp;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
@@ -11,32 +10,41 @@ public static partial class DumpDataHelper
|
||||
[GeneratedRegex(@"\([^)]*\)")]
|
||||
private static partial Regex MatchBrackets();
|
||||
|
||||
|
||||
public static async Task<string[]> GetCsvHeadersFromSqlFileAsync(string txt)
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的SQL文件内容中读取表头
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static string[] GetCsvHeadersFromSqlFile(string content)
|
||||
{
|
||||
//var txt = await File.ReadAllTextAsync(filePath);
|
||||
var match = MatchBrackets().Match(txt);
|
||||
|
||||
var match = MatchBrackets().Match(content);
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("输入的SQL内容有误,无法提取表头", nameof(content));
|
||||
return ParseHeader(match.ValueSpan);
|
||||
}
|
||||
|
||||
private static string[] ParseHeader(ReadOnlySpan<char> headerStr)
|
||||
{
|
||||
headerStr = headerStr[1..^1];
|
||||
Span<Range> ranges = stackalloc Range[50];
|
||||
var count = headerStr.Split(ranges, ',');
|
||||
var arr = new string[count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
|
||||
string[] ParseHeader(ReadOnlySpan<char> headerStr)
|
||||
{
|
||||
arr[i] = headerStr[ranges[i]].Trim("@`").ToString(); // 消除列名的反引号,如果是变量则消除@
|
||||
}
|
||||
headerStr = headerStr[1..^1];
|
||||
Span<Range> ranges = stackalloc Range[50];
|
||||
var count = headerStr.Split(ranges, ',');
|
||||
var arr = new string[count];
|
||||
|
||||
return arr;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
arr[i] = headerStr[ranges[i]].Trim("@`").ToString(); // 消除列名的反引号,如果是变量则消除@
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string GetTableName(ReadOnlySpan<char> filePath)
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的Csv文件名解析出表名
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetTableNameFromCsvFileName(ReadOnlySpan<char> filePath)
|
||||
{
|
||||
filePath = filePath[(filePath.LastIndexOf('\\') + 1)..];
|
||||
var firstDotIdx = -1;
|
||||
@@ -60,13 +68,24 @@ public static partial class DumpDataHelper
|
||||
return filePath[(firstDotIdx+1)..secondDotIdx].ToString();
|
||||
}
|
||||
|
||||
public static async Task<string[]> GetCsvFileNamesFromSqlFileAsync(string txt,Regex regex)
|
||||
/// <summary>
|
||||
/// 从MyDumper导出的SQL文件内容中读取CSV文件名
|
||||
/// </summary>
|
||||
/// <param name="txt"></param>
|
||||
/// <param name="regex"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<string[]> GetCsvFileNamesFromSqlFileAsync(string txt, Regex regex)
|
||||
{
|
||||
//var txt = await File.ReadAllTextAsync(filePath);
|
||||
var matches = regex.Matches(txt);
|
||||
return matches.Select(match => match.ValueSpan[1..^1].ToString()).ToArray();
|
||||
return Task.FromResult(matches.Select(match => match.ValueSpan[1..^1].ToString()).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字符串是否为16进制
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckHexField(string? str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
@@ -90,9 +109,17 @@ public static partial class DumpDataHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将输入流以ZSTD标准解压为字符串
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> DecompressZstAsStringAsync(Stream stream)
|
||||
{
|
||||
await using var ds = new DecompressionStream(stream);
|
||||
var reader = new StreamReader(ds);
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
// public static string EliminateEscapeChars(ReadOnlySpan<char> str)
|
||||
// {
|
||||
// char[] escapeChars = ['0','\''];
|
||||
// }
|
||||
}
|
67
ConsoleApp2/Helpers/Extensions.IEnumerable.cs
Normal file
67
ConsoleApp2/Helpers/Extensions.IEnumerable.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static string ToMarkdownTable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]T>(this IEnumerable<T> source)
|
||||
{
|
||||
var properties = typeof(T).GetRuntimeProperties();
|
||||
var fields = typeof(T)
|
||||
.GetRuntimeFields()
|
||||
.Where(f => f.IsPublic);
|
||||
|
||||
var gettables = Enumerable.Union(
|
||||
properties.Select(p => new { p.Name, GetValue = (Func<object, object>)p.GetValue, Type = p.PropertyType }),
|
||||
fields.Select(p => new { p.Name, GetValue = (Func<object, object>)p.GetValue, Type = p.FieldType }));
|
||||
|
||||
var maxColumnValues = source
|
||||
.Select(x => gettables.Select(p => p.GetValue(x)?.ToString()?.Length ?? 0))
|
||||
.Union(new[] { gettables.Select(p => p.Name.Length) }) // Include header in column sizes
|
||||
.Aggregate(
|
||||
new int[gettables.Count()].AsEnumerable(),
|
||||
(accumulate, x) => accumulate.Zip(x, Math.Max))
|
||||
.ToArray();
|
||||
|
||||
var columnNames = gettables.Select(p => p.Name);
|
||||
|
||||
var headerLine = "| " + string.Join(" | ", columnNames.Select((n, i) => n.PadRight(maxColumnValues[i]))) + " |";
|
||||
|
||||
var isNumeric = new Func<Type, bool>(type =>
|
||||
type == typeof(Byte) ||
|
||||
type == typeof(SByte) ||
|
||||
type == typeof(UInt16) ||
|
||||
type == typeof(UInt32) ||
|
||||
type == typeof(UInt64) ||
|
||||
type == typeof(Int16) ||
|
||||
type == typeof(Int32) ||
|
||||
type == typeof(Int64) ||
|
||||
type == typeof(Decimal) ||
|
||||
type == typeof(Double) ||
|
||||
type == typeof(Single));
|
||||
|
||||
var rightAlign = new Func<Type, char>(type => isNumeric(type) ? ':' : ' ');
|
||||
|
||||
var headerDataDividerLine =
|
||||
"| " +
|
||||
string.Join(
|
||||
"| ",
|
||||
gettables.Select((g, i) => new string('-', maxColumnValues[i]) + rightAlign(g.Type))) +
|
||||
"|";
|
||||
|
||||
var lines = new[]
|
||||
{
|
||||
headerLine,
|
||||
headerDataDividerLine,
|
||||
}.Union(
|
||||
source
|
||||
.Select(s =>
|
||||
"| " + string.Join(" | ",
|
||||
gettables.Select((n, i) => (n.GetValue(s)?.ToString() ?? "").PadRight(maxColumnValues[i]))) +
|
||||
" |"));
|
||||
|
||||
return lines
|
||||
.Aggregate((p, c) => p + Environment.NewLine + c);
|
||||
}
|
||||
}
|
@@ -5,6 +5,12 @@ namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 截断字符串
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="maxLength">截断长度</param>
|
||||
/// <returns></returns>
|
||||
public static string Omit(this ReadOnlySpan<char> @this, int maxLength)
|
||||
{
|
||||
if (@this.Length > maxLength)
|
||||
@@ -12,8 +18,20 @@ public static class StringExtensions
|
||||
return @this.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 截断字符串
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="maxLength">截断长度</param>
|
||||
/// <returns></returns>
|
||||
public static string Omit(this string @this, int maxLength) => Omit(@this.AsSpan(), maxLength);
|
||||
|
||||
/// <summary>
|
||||
/// 将16进制字符串转换为字符串
|
||||
/// </summary>
|
||||
/// <param name="hexString"></param>
|
||||
/// <param name="encoding"></param>
|
||||
/// <returns></returns>
|
||||
public static string FromHex(ReadOnlySpan<char> hexString, Encoding? encoding = null)
|
||||
{
|
||||
encoding ??= Encoding.UTF8;
|
||||
@@ -38,6 +56,11 @@ public static class StringExtensions
|
||||
return encoding.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为JSON字符串
|
||||
/// </summary>
|
||||
/// <param name="hexStr"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckJsonHex(ReadOnlySpan<char> hexStr)
|
||||
{
|
||||
if (hexStr.Length < 2)
|
||||
|
19
ConsoleApp2/Helpers/Extensions.Task.cs
Normal file
19
ConsoleApp2/Helpers/Extensions.Task.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace ConsoleApp2.Helpers;
|
||||
|
||||
public static class TaskExtensions
|
||||
{
|
||||
public static async Task WaitUntil(Func<bool> condition, int pollDelay = 25, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!condition())
|
||||
{
|
||||
await Task.Delay(pollDelay, ct);
|
||||
}
|
||||
}
|
||||
catch(TaskCanceledException)
|
||||
{
|
||||
// CancellationToken激活时Task.Delay通过抛异常来结束停止等待,不用管它
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ConsoleApp2.Helpers
|
||||
{
|
||||
public static class ValidateConsole
|
||||
{
|
||||
public static void ValidateInput<T>(Func<string,bool> converter,string message)
|
||||
{
|
||||
Console.Write(message);
|
||||
string ? input = Console.ReadLine();
|
||||
while (true)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
{
|
||||
var result = converter(input);
|
||||
if (result == false)
|
||||
{
|
||||
Console.WriteLine($"输入的内容不合法,请重新输入!");
|
||||
input = Console.ReadLine();
|
||||
}
|
||||
else break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user