mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
Merge branch 'main' into dev
This commit is contained in:
commit
d005a1b4cc
41
src/.editorconfig
Normal file
41
src/.editorconfig
Normal file
@ -0,0 +1,41 @@
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
|
||||
# ReSharper properties
|
||||
resharper_align_linq_query = true
|
||||
resharper_align_multiline_argument = true
|
||||
resharper_align_multiline_array_and_object_initializer = true
|
||||
resharper_align_multiline_binary_patterns = true
|
||||
resharper_align_multiline_calls_chain = true
|
||||
resharper_align_multiline_extends_list = true
|
||||
resharper_align_multiline_parameter = true
|
||||
resharper_align_multiline_property_pattern = true
|
||||
resharper_align_multiline_switch_expression = true
|
||||
resharper_align_multiple_declaration = true
|
||||
resharper_align_multline_type_parameter_constrains = true
|
||||
resharper_align_multline_type_parameter_list = true
|
||||
resharper_align_tuple_components = true
|
||||
resharper_allow_comment_after_lbrace = true
|
||||
resharper_csharp_empty_block_style = together_same_line
|
||||
resharper_csharp_outdent_commas = true
|
||||
resharper_csharp_stick_comment = false
|
||||
resharper_csharp_wrap_before_comma = true
|
||||
resharper_indent_nested_foreach_stmt = true
|
||||
resharper_indent_nested_for_stmt = true
|
||||
resharper_indent_nested_while_stmt = true
|
||||
resharper_indent_preprocessor_if = usual_indent
|
||||
resharper_indent_preprocessor_other = usual_indent
|
||||
resharper_int_align = true
|
||||
resharper_keep_existing_arrangement = false
|
||||
resharper_place_linq_into_on_new_line = false
|
||||
resharper_place_simple_switch_expression_on_single_line = true
|
||||
resharper_wrap_before_eq = true
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_wrap_switch_expression = chop_if_long
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_indent_braces = false
|
||||
csharp_new_line_before_open_brace = local_functions, methods, types
|
113
src/Convert2Lf/Main.cs
Normal file
113
src/Convert2Lf/Main.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dot.Convert2Lf;
|
||||
|
||||
public sealed class Main : Tool<Option>, IDisposable
|
||||
{
|
||||
private int _breakCnt;
|
||||
private bool _disposed;
|
||||
private static readonly object _lockObj = new();
|
||||
private int _procedCnt;
|
||||
private int _replaceCnt;
|
||||
private ChildProgressBar _step2Bar;
|
||||
private int _totalCnt;
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
|
||||
~Main()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing) _step2Bar?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private void FileHandle(string file)
|
||||
{
|
||||
_step2Bar.Tick();
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
var tmpFile = $"{file}.tmp";
|
||||
var isReplaced = false;
|
||||
var isBin = false;
|
||||
int data;
|
||||
|
||||
using (var fsr = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
|
||||
using var fsw = new FileStream(tmpFile, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
|
||||
while ((data = fsr.ReadByte()) != -1) {
|
||||
switch (data) {
|
||||
case 0x0d when fsr.ReadByte() == 0x0a: // crlf windows
|
||||
fsw.WriteByte(0x0a);
|
||||
isReplaced = true;
|
||||
continue;
|
||||
case 0x0d: //cr macos
|
||||
fsw.WriteByte(0x0a);
|
||||
fsr.Seek(-1, SeekOrigin.Current);
|
||||
isReplaced = true;
|
||||
continue;
|
||||
case 0x00 or 0xff: //非文本文件
|
||||
isBin = true;
|
||||
break;
|
||||
default:
|
||||
fsw.WriteByte((byte)data);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isReplaced && !isBin) {
|
||||
MoveFile(tmpFile, file);
|
||||
|
||||
ShowMessage(0, 1, 0);
|
||||
}
|
||||
else {
|
||||
File.Delete(tmpFile);
|
||||
ShowMessage(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int replaceCnt, int breakCnt)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_replaceCnt += replaceCnt;
|
||||
_breakCnt += breakCnt;
|
||||
_step2Bar.Message = $"已处理:{_procedCnt}/{_totalCnt},替换:{_replaceCnt},跳过:{_breakCnt}";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override void Run()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path)) throw new ArgumentException(nameof(Opt.Path), $"指定的路径“{Opt.Path}”不存在");
|
||||
|
||||
|
||||
using var step1Bar = new IndeterminateProgressBar("查找文件...", DefaultProgressBarOptions);
|
||||
|
||||
|
||||
var fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
|
||||
step1Bar.Message = "查找文件...OK";
|
||||
step1Bar.Finished();
|
||||
|
||||
_step2Bar = step1Bar.Spawn(_totalCnt, string.Empty, DefaultProgressBarOptions);
|
||||
|
||||
Parallel.ForEach(fileList, FileHandle);
|
||||
}
|
||||
}
|
11
src/Convert2Lf/Option.cs
Normal file
11
src/Convert2Lf/Option.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Dot.Convert2Lf;
|
||||
|
||||
[Verb("convert-lf", HelpText = "换行符转换为lf")]
|
||||
public class Option : IOption
|
||||
{
|
||||
[Option('f', "filter", Required = false, HelpText = "文件通配符", Default = "*.*")]
|
||||
public string Filter { get; set; } //normal options here
|
||||
|
||||
[Option('p', "path", Required = false, HelpText = "要处理的目录路径", Default = ".")]
|
||||
public string Path { get; set; }
|
||||
}
|
66
src/Random/Main.cs
Normal file
66
src/Random/Main.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NSExt.Extensions;
|
||||
using TextCopy;
|
||||
|
||||
namespace Dot.Random;
|
||||
|
||||
public sealed partial class Main : Tool<Option>
|
||||
{
|
||||
private readonly char[][] _charTable = {
|
||||
"0123456789".ToCharArray() //
|
||||
, "abcdefghijklmnopqrstuvwxyz".ToCharArray()
|
||||
, "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
|
||||
, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".ToCharArray()
|
||||
};
|
||||
|
||||
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
[GeneratedRegex("[a-z]")]
|
||||
private static partial Regex RegexLowerCaseLetter();
|
||||
|
||||
[GeneratedRegex("\\d")]
|
||||
private static partial Regex RegexNumber();
|
||||
|
||||
[GeneratedRegex("[^\\da-zA-Z]")]
|
||||
private static partial Regex RegexSpecialCharacter();
|
||||
|
||||
[GeneratedRegex("[A-Z]")]
|
||||
private static partial Regex RegexUpperCaseLetter();
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
unsafe {
|
||||
var pSource = stackalloc char[_charTable.Sum(x => x.Length)];
|
||||
var pDest = stackalloc char[Opt.Length];
|
||||
var sourceLen = 0;
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.Number))
|
||||
foreach (var c in _charTable[0])
|
||||
*(pSource + sourceLen++) = c;
|
||||
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.LowerCaseLetter))
|
||||
foreach (var c in _charTable[1])
|
||||
*(pSource + sourceLen++) = c;
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.UpperCaseLetter))
|
||||
foreach (var c in _charTable[2])
|
||||
*(pSource + sourceLen++) = c;
|
||||
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.SpecialCharacter))
|
||||
foreach (var c in _charTable[3])
|
||||
*(pSource + sourceLen++) = c;
|
||||
|
||||
|
||||
var randScope = new[] { 0, sourceLen };
|
||||
for (var i = 0; i != Opt.Length; ++i) //
|
||||
*(pDest + i) = *(pSource + randScope.Rand());
|
||||
|
||||
var result = new string(pDest, 0, Opt.Length);
|
||||
ClipboardService.SetText(result);
|
||||
Console.WriteLine($"已复制到剪贴板:{result}");
|
||||
}
|
||||
}
|
||||
}
|
21
src/Random/Option.cs
Normal file
21
src/Random/Option.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace Dot.Random;
|
||||
|
||||
[Verb("rand", HelpText = "随机数生成器")]
|
||||
public class Option : IOption
|
||||
{
|
||||
[Flags]
|
||||
public enum GenerateTypes
|
||||
{
|
||||
Number = 1
|
||||
, LowerCaseLetter = 2
|
||||
, UpperCaseLetter = 4
|
||||
, SpecialCharacter = 8
|
||||
}
|
||||
|
||||
[Value(1, MetaName = "长度", Required = true, HelpText = "随机数字长度")]
|
||||
public int Length { get; set; }
|
||||
|
||||
|
||||
[Value(0, MetaName = "生成类型", Required = true, HelpText = "BitSet 1:[0-9],2:[a-z],4:[A-Z],8:[ascii.0x21-0x2F]")]
|
||||
public GenerateTypes Type { get; set; }
|
||||
}
|
103
src/RemoveTrailingWhiteSpace/Main.cs
Normal file
103
src/RemoveTrailingWhiteSpace/Main.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NSExt.Extensions;
|
||||
|
||||
namespace Dot.RemoveTrailingWhiteSpace;
|
||||
|
||||
public sealed class Main : Tool<Option>, IDisposable
|
||||
{
|
||||
private int _breakCnt;
|
||||
private bool _disposed;
|
||||
private static readonly object _lockObj = new();
|
||||
private int _procedCnt;
|
||||
private int _replaceCnt;
|
||||
private ChildProgressBar _step2Bar;
|
||||
private int _totalCnt;
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
|
||||
~Main()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing) _step2Bar?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private void FileHandle(string file)
|
||||
{
|
||||
_step2Bar.Tick();
|
||||
ShowMessage(1, 0, 0);
|
||||
var spacesCnt = 0;
|
||||
|
||||
using var fsr = OpenFileToWrite(file);
|
||||
if (fsr.Length == 0 || (spacesCnt = GetSpacesCnt(fsr)) == 0) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
fsr.Seek(0, SeekOrigin.Begin);
|
||||
if (!fsr.IsTextStream()) return;
|
||||
ShowMessage(0, 1, 0);
|
||||
fsr.SetLength(fsr.Length - spacesCnt);
|
||||
}
|
||||
|
||||
private static int GetSpacesCnt(Stream fsr)
|
||||
{
|
||||
var trimLen = 0;
|
||||
fsr.Seek(-1, SeekOrigin.End);
|
||||
int data;
|
||||
while ((data = fsr.ReadByte()) != -1)
|
||||
if (new[] { 0x20, 0x0d, 0x0a }.Contains(data)) {
|
||||
++trimLen;
|
||||
if (fsr.Position - 2 < 0) break;
|
||||
fsr.Seek(-2, SeekOrigin.Current);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
return trimLen;
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int removeCnt, int breakCnt)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_replaceCnt += removeCnt;
|
||||
_breakCnt += breakCnt;
|
||||
_step2Bar.Message = $"已处理:{_procedCnt}/{_totalCnt},替换:{_replaceCnt},跳过:{_breakCnt}";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override void Run()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path)) throw new ArgumentException(nameof(Opt.Path), $"指定的路径“{Opt.Path}”不存在");
|
||||
|
||||
|
||||
using var step1Bar = new IndeterminateProgressBar("查找文件...", DefaultProgressBarOptions);
|
||||
|
||||
|
||||
var fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
|
||||
step1Bar.Message = "查找文件...OK";
|
||||
step1Bar.Finished();
|
||||
|
||||
_step2Bar = step1Bar.Spawn(_totalCnt, string.Empty, DefaultProgressBarOptions);
|
||||
|
||||
Parallel.ForEach(fileList, FileHandle);
|
||||
}
|
||||
}
|
11
src/RemoveTrailingWhiteSpace/Option.cs
Normal file
11
src/RemoveTrailingWhiteSpace/Option.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Dot.RemoveTrailingWhiteSpace;
|
||||
|
||||
[Verb("remove-whitespace", HelpText = "移除文件尾部换行和空格")]
|
||||
public class Option : IOption
|
||||
{
|
||||
[Option('f', "filter", Required = false, HelpText = "文件通配符", Default = "*.*")]
|
||||
public string Filter { get; set; } //normal options here
|
||||
|
||||
[Option('p', "path", Required = false, HelpText = "要处理的目录路径", Default = ".")]
|
||||
public string Path { get; set; }
|
||||
}
|
@ -102,9 +102,7 @@ html-decode: {o.HtmlDecode}
|
||||
PrintOutput(utf8);
|
||||
PrintOutput(unicodeLittleEndian);
|
||||
PrintOutput(unicodeBigEndian);
|
||||
|
||||
Console.Write(Strings.PressAnyKey);
|
||||
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
@ -61,7 +61,6 @@ public abstract class Tool<TOption> : ITool
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
|
||||
return fsr;
|
||||
}
|
||||
|
||||
|
90
src/TrimUtf8Bom/Main.cs
Normal file
90
src/TrimUtf8Bom/Main.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dot.TrimUtf8Bom;
|
||||
|
||||
public sealed class Main : Tool<Option>, IDisposable
|
||||
{
|
||||
private int _breakCnt;
|
||||
private bool _disposed;
|
||||
private static readonly object _lockObj = new();
|
||||
private int _procedCnt;
|
||||
private ChildProgressBar _step2Bar;
|
||||
private int _totalCnt;
|
||||
private int _trimCnt;
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
|
||||
~Main()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing) _step2Bar?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int replaceCnt, int breakCnt)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_trimCnt += replaceCnt;
|
||||
_breakCnt += breakCnt;
|
||||
_step2Bar.Message = $"已处理:{_procedCnt}/{_totalCnt},移除BOM:{_trimCnt},跳过:{_breakCnt}";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override void Run()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path)) throw new ArgumentException(nameof(Opt.Path), $"指定的路径“{Opt.Path}”不存在");
|
||||
|
||||
|
||||
var utf8Bom = new byte[] { 0xef, 0xbb, 0xbf };
|
||||
using var step1Bar = new IndeterminateProgressBar("查找文件...", DefaultProgressBarOptions);
|
||||
|
||||
|
||||
var fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
|
||||
step1Bar.Message = "查找文件...OK";
|
||||
step1Bar.Finished();
|
||||
|
||||
_step2Bar = step1Bar.Spawn(_totalCnt, string.Empty, DefaultProgressBarOptions);
|
||||
|
||||
Parallel.ForEach(fileList, file => {
|
||||
_step2Bar.Tick();
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
var tmpFile = $"{file}.tmp";
|
||||
var isReplaced = false;
|
||||
using (var fsr = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
|
||||
Span<byte> buffer = stackalloc byte[utf8Bom.Length];
|
||||
var readLen = fsr.Read(buffer);
|
||||
if (readLen == utf8Bom.Length && buffer.SequenceEqual(utf8Bom)) {
|
||||
using var fsw = new FileStream(tmpFile, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
int data;
|
||||
while ((data = fsr.ReadByte()) != -1) fsw.WriteByte((byte)data);
|
||||
isReplaced = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isReplaced) {
|
||||
MoveFile(tmpFile, file);
|
||||
ShowMessage(0, 1, 0);
|
||||
}
|
||||
else {
|
||||
ShowMessage(0, 0, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
11
src/TrimUtf8Bom/Option.cs
Normal file
11
src/TrimUtf8Bom/Option.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Dot.TrimUtf8Bom;
|
||||
|
||||
[Verb("trim-utf8-bom", HelpText = "移除文件的uf8 bom")]
|
||||
public class Option : IOption
|
||||
{
|
||||
[Option('f', "filter", Required = false, HelpText = "文件通配符", Default = "*.*")]
|
||||
public string Filter { get; set; } //normal options here
|
||||
|
||||
[Option('p', "path", Required = false, HelpText = "要处理的目录路径", Default = ".")]
|
||||
public string Path { get; set; }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user