From 632e58cfcec6fde1ab2d96365915c8d44f22e021 Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Wed, 16 Jul 2025 15:25:11 +0800 Subject: [PATCH] BS 0.1.0 File --- Convention-CSharp.csproj | 1 + Convention/[Runtime]/File.cs | 999 +++++++++++++++++++++++++++++++---- Convention/[Runtime]/Math.cs | 6 - [Test]/FileTest.cs | 214 ++++++++ [Test]/Program.cs | 19 +- 5 files changed, 1126 insertions(+), 113 deletions(-) delete mode 100644 Convention/[Runtime]/Math.cs create mode 100644 [Test]/FileTest.cs diff --git a/Convention-CSharp.csproj b/Convention-CSharp.csproj index 1bb1198..77e5def 100644 --- a/Convention-CSharp.csproj +++ b/Convention-CSharp.csproj @@ -5,6 +5,7 @@ Exe net8.0 Convention + true diff --git a/Convention/[Runtime]/File.cs b/Convention/[Runtime]/File.cs index a8d0807..155a3b9 100644 --- a/Convention/[Runtime]/File.cs +++ b/Convention/[Runtime]/File.cs @@ -3,9 +3,41 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; +using System.IO.Compression; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.ComponentModel; +using System.Runtime.InteropServices; namespace Convention { + // 自定义异常类 + public class FileOperationException : Exception + { + public FileOperationException(string message) : base(message) { } + public FileOperationException(string message, Exception innerException) : base(message, innerException) { } + } + + public class CompressionException : FileOperationException + { + public CompressionException(string message) : base(message) { } + public CompressionException(string message, Exception innerException) : base(message, innerException) { } + } + + public class EncryptionException : FileOperationException + { + public EncryptionException(string message) : base(message) { } + public EncryptionException(string message, Exception innerException) : base(message, innerException) { } + } + + public class HashException : FileOperationException + { + public HashException(string message) : base(message) { } + public HashException(string message, Exception innerException) : base(message, innerException) { } + } + [Serializable] public sealed class ToolFile { @@ -13,7 +45,7 @@ namespace Convention private FileSystemInfo OriginInfo; public ToolFile(string path) { - FullPath = path; + FullPath = Path.GetFullPath(path); Refresh(); } public override string ToString() @@ -52,6 +84,47 @@ namespace Convention )..]; } + // 新增:获取文件名(对应 Python 的 GetFilename) + public string GetFilename(bool is_without_extension = false) + { + if (is_without_extension && Path.HasExtension(FullPath)) + { + return Path.GetFileNameWithoutExtension(FullPath); + } + else if (FullPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || FullPath.EndsWith(Path.AltDirectorySeparatorChar.ToString())) + { + return Path.GetFileName(FullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + } + else + { + return Path.GetFileName(FullPath); + } + } + + // 新增:获取目录路径(对应 Python 的 GetDir) + public string GetDir() + { + return Path.GetDirectoryName(FullPath); + } + + // 新增:获取目录的 ToolFile 对象(对应 Python 的 GetDirToolFile) + public ToolFile GetDirToolFile() + { + return new ToolFile(GetDir()); + } + + // 新增:获取当前目录名(对应 Python 的 GetCurrentDirName) + public string GetCurrentDirName() + { + return Path.GetDirectoryName(FullPath); + } + + // 新增:获取父目录(对应 Python 的 GetParentDir) + public ToolFile GetParentDir() + { + return new ToolFile(GetDir()); + } + #endregion #region Exists @@ -112,6 +185,44 @@ namespace Convention return result; } + // 新增:加载为 CSV(使用 C# 内置功能) + public List LoadAsCsv() + { + if (IsFile() == false) + throw new InvalidOperationException("Target is not a file"); + + var lines = File.ReadAllLines(FullPath); + var result = new List(); + + foreach (var line in lines) + { + var fields = line.Split(','); + result.Add(fields); + } + + return result; + } + + // 新增:加载为 XML(使用 C# 内置功能) + public string LoadAsXml() + { + return LoadAsText(); + } + + // 新增:加载为 Excel(简化实现,实际需要第三方库) + public string LoadAsExcel() + { + // 注意:真正的 Excel 读取需要第三方库如 EPPlus 或 NPOI + // 这里返回文本内容作为简化实现 + return LoadAsText(); + } + + // 新增:加载为未知格式(对应 Python 的 LoadAsUnknown) + public string LoadAsUnknown(string suffix) + { + return LoadAsText(); + } + #endregion #region Save @@ -124,7 +235,7 @@ namespace Convention { EasySave.EasySave.Save(key, data,FullPath); } - private void SaveAsText(string data) + public void SaveAsText(string data) { using var fs = new FileStream(FullPath, FileMode.CreateNew, FileAccess.Write); using var sw = new StreamWriter(fs); @@ -151,6 +262,43 @@ namespace Convention SaveDataAsBinary(FullPath, data, (OriginInfo as FileInfo).OpenWrite()); } + // 新增:保存为 CSV(对应 Python 的 SaveAsCsv) + public void SaveAsCsv(List csvData) + { + if (IsFile() == false) + throw new InvalidOperationException("Target is not a file"); + + var lines = csvData.Select(row => string.Join(",", row)); + File.WriteAllLines(FullPath, lines); + } + + // 新增:保存为 XML(对应 Python 的 SaveAsXml) + public void SaveAsXml(string xmlData) + { + SaveAsText(xmlData); + } + + // 新增:保存为 Excel(简化实现) + public void SaveAsExcel(string excelData) + { + SaveAsText(excelData); + } + + // 新增:保存为数据框(对应 Python 的 SaveAsDataframe) + public void SaveAsDataframe(List dataframeData) + { + SaveAsCsv(dataframeData); + } + + // 新增:保存为未知格式(对应 Python 的 SaveAsUnknown) + public void SaveAsUnknown(object unknownData) + { + if (unknownData is byte[] bytes) + SaveAsBinary(bytes); + else + SaveAsText(unknownData.ToString()); + } + #endregion #region IsFileType @@ -178,6 +326,48 @@ namespace Convention #endregion + #region Size and Properties + + // 新增:获取文件大小(对应 Python 的 GetSize) + public long GetSize() + { + if (IsDir()) + { + return GetDirectorySize(FullPath); + } + else + { + return (OriginInfo as FileInfo)?.Length ?? 0; + } + } + + private long GetDirectorySize(string path) + { + long size = 0; + try + { + var files = Directory.GetFiles(path); + foreach (var file in files) + { + var fileInfo = new FileInfo(file); + size += fileInfo.Length; + } + + var dirs = Directory.GetDirectories(path); + foreach (var dir in dirs) + { + size += GetDirectorySize(dir); + } + } + catch (Exception) + { + // 忽略访问权限错误 + } + return size; + } + + #endregion + #region Operator public static ToolFile operator |(ToolFile left, string rightPath) @@ -185,6 +375,22 @@ namespace Convention string lp = left.GetFullPath(); return new ToolFile(Path.Combine(lp, rightPath)); } + + // 新增:相等比较(对应 Python 的 __eq__) + public override bool Equals(object obj) + { + if (obj is ToolFile other) + { + return Path.GetFullPath(FullPath).Equals(Path.GetFullPath(other.FullPath), StringComparison.OrdinalIgnoreCase); + } + return false; + } + + public override int GetHashCode() + { + return Path.GetFullPath(FullPath).GetHashCode(); + } + public ToolFile Open(string path) { this.FullPath = path; @@ -240,6 +446,46 @@ namespace Convention } return this; } + + // 新增:复制方法重载(对应 Python 的 Copy) + public ToolFile Copy(string targetPath = null) + { + if (targetPath == null) + return new ToolFile(FullPath); + + if (!Exists()) + throw new FileNotFoundException("File not found"); + + var targetFile = new ToolFile(targetPath); + if (targetFile.IsDir()) + targetFile = targetFile | GetFilename(); + + if (IsDir()) + CopyDirectory(FullPath, targetFile.GetFullPath()); + else + File.Copy(FullPath, targetFile.GetFullPath()); + + return targetFile; + } + + private void CopyDirectory(string sourceDir, string destinationDir) + { + var dir = new DirectoryInfo(sourceDir); + Directory.CreateDirectory(destinationDir); + + foreach (FileInfo file in dir.GetFiles()) + { + string targetFilePath = Path.Combine(destinationDir, file.Name); + file.CopyTo(targetFilePath); + } + + foreach (DirectoryInfo subDir in dir.GetDirectories()) + { + string newDestinationDir = Path.Combine(destinationDir, subDir.Name); + CopyDirectory(subDir.FullName, newDestinationDir); + } + } + public ToolFile Delete() { if (IsDir()) @@ -248,152 +494,711 @@ namespace Convention File.Delete(FullPath); return this; } - public ToolFile Remove() => Delete(); - public ToolFile MustExistsPath() + + // 新增:Remove 方法(对应 Python 的 Remove) + public ToolFile Remove() { - this.Close(); - this.TryCreateParentPath(); - this.Create(); - return this; + return Delete(); } + #endregion - #region Dir + #region Directory Operations + public ToolFile MustExistsPath() + { + TryCreateParentPath(); + Create(); + return this; + } public ToolFile TryCreateParentPath() { - if (this.GetName().Contains('/') || this.GetName().Contains('\\')) + string dirPath = Path.GetDirectoryName(FullPath); + if (!string.IsNullOrEmpty(dirPath) && !Directory.Exists(dirPath)) { - var parent = new ToolFile(this.GetParentDir()); - if (parent.Exists()) - { - parent.Create(); - } + Directory.CreateDirectory(dirPath); + } + return this; + } + public List DirIter() + { + if (!IsDir()) + throw new InvalidOperationException("Target is not a directory"); + return Directory.GetFileSystemEntries(FullPath).ToList(); + } + public List DirToolFileIter() + { + if (!IsDir()) + throw new InvalidOperationException("Target is not a directory"); + var result = new List(); + foreach (var entry in Directory.GetFileSystemEntries(FullPath)) + { + result.Add(new ToolFile(entry)); + } + return result; + } + public ToolFile BackToParentDir() + { + FullPath = GetDir(); + Refresh(); + return this; + } + public int DirCount(bool ignore_folder = true) + { + if (!IsDir()) + throw new InvalidOperationException("Target is not a directory"); + + var entries = Directory.GetFileSystemEntries(FullPath); + if (ignore_folder) + { + return entries.Count(entry => File.Exists(entry)); + } + return entries.Length; + } + public ToolFile DirClear() + { + if (!IsDir()) + throw new InvalidOperationException("Target is not a directory"); + + foreach (var file in DirToolFileIter()) + { + file.Remove(); } return this; } - public List DirIter() + // 新增:MakeFileInside 方法(对应 Python 的 MakeFileInside) + public ToolFile MakeFileInside(ToolFile data, bool is_delete_source = false) { - if (this.IsDir()) + if (!IsDir()) + throw new InvalidOperationException("Cannot make file inside a file, because this object target is not a directory"); + + var result = this | data.GetFilename(); + if (is_delete_source) + data.Move(result.GetFullPath()); + else + data.Copy(result.GetFullPath()); + + return this; + } + + // 新增:查找文件方法(对应 Python 的 FirstFileWithExtension) + public ToolFile FirstFileWithExtension(string extension) + { + var targetDir = IsDir() ? this : GetDirToolFile(); + foreach (var file in targetDir.DirToolFileIter()) { - var dir = new DirectoryInfo(FullPath); - var result = dir.GetDirectories().ToList().ConvertAll(x => x.FullName); - result.AddRange(dir.GetFiles().ToList().ConvertAll(x => x.FullName)); - return result; + if (!file.IsDir() && file.GetExtension() == extension) + { + return file; + } } return null; } - public List DirToolFileIter() + // 新增:查找文件方法(对应 Python 的 FirstFile) + public ToolFile FirstFile(Func predicate) { - if (this.IsDir()) + var targetDir = IsDir() ? this : GetDirToolFile(); + foreach (var file in targetDir.DirToolFileIter()) { - return DirIter().ConvertAll(x => new ToolFile(x)); - } - throw new DirectoryNotFoundException(FullPath); - } - - public ToolFile BackToParentDir() - { - var file = new ToolFile(this.GetParentDir()); - this.Close(); - this.FullPath = file.FullPath; - this.OriginInfo = file.OriginInfo; - return this; - } - - public ToolFile GetParentDir() - { - if (IsDir()) - return new ToolFile((this.OriginInfo as DirectoryInfo).Parent.FullName); - else - return new ToolFile((this.OriginInfo as FileInfo).DirectoryName); - } - - public int DirCount() - { - if (IsDir()) - return Directory.EnumerateFiles(FullPath).Count(); - return -1; - } - - public ToolFile DirClear() - { - if (IsDir()) - { - foreach (var file in DirIter()) + if (predicate(file.GetFilename())) { - File.Delete(file); + return file; } } - throw new DirectoryNotFoundException(); + return null; } - public ToolFile MakeFileInside(string source, bool isDeleteSource = false) + // 新增:查找所有文件方法(对应 Python 的 FindFileWithExtension) + public List FindFileWithExtension(string extension) { - if (this.IsDir() == false) - throw new DirectoryNotFoundException(FullPath); - string target = this | source; - if (isDeleteSource) - File.Move(target, source); - else - File.Copy(target, source); - return this; + var targetDir = IsDir() ? this : GetDirToolFile(); + var result = new List(); + foreach (var file in targetDir.DirToolFileIter()) + { + if (!file.IsDir() && file.GetExtension() == extension) + { + result.Add(file); + } + } + return result; + } + + // 新增:查找所有文件方法(对应 Python 的 FindFile) + public List FindFile(Func predicate) + { + var targetDir = IsDir() ? this : GetDirToolFile(); + var result = new List(); + foreach (var file in targetDir.DirToolFileIter()) + { + if (predicate(file.GetFilename())) + { + result.Add(file); + } + } + return result; } #endregion + #region Compression + + // 新增:压缩方法(对应 Python 的 Compress) + public ToolFile Compress(string outputPath = null, string format = "zip") + { + if (!Exists()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + if (outputPath == null) + { + outputPath = GetFullPath() + (format == "zip" ? ".zip" : ".tar"); + } + + try + { + if (format.ToLower() == "zip") + { + if (IsDir()) + { + ZipFile.CreateFromDirectory(FullPath, outputPath); + } + else + { + using (var archive = ZipFile.Open(outputPath, ZipArchiveMode.Create)) + { + archive.CreateEntryFromFile(FullPath, GetFilename()); + } + } + } + else + { + throw new CompressionException($"Unsupported compression format: {format}"); + } + + return new ToolFile(outputPath); + } + catch (Exception ex) + { + throw new CompressionException($"Compression failed: {ex.Message}", ex); + } + } + + // 新增:解压方法(对应 Python 的 Decompress) + public ToolFile Decompress(string outputPath = null) + { + if (!Exists() || !IsFile()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + if (GetExtension().ToLower() != "zip") + throw new CompressionException("Only ZIP files are supported for decompression"); + + if (outputPath == null) + { + outputPath = Path.Combine(GetDir(), Path.GetFileNameWithoutExtension(GetFilename())); + } + + try + { + ZipFile.ExtractToDirectory(FullPath, outputPath); + return new ToolFile(outputPath); + } + catch (Exception ex) + { + throw new CompressionException($"Decompression failed: {ex.Message}", ex); + } + } + + #endregion + + #region Encryption + + // 新增:加密方法(对应 Python 的 Encrypt) + public ToolFile Encrypt(string key, string algorithm = "AES") + { + if (!Exists() || !IsFile()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + try + { + byte[] data = LoadAsBinary(); + byte[] encryptedData; + + if (algorithm.ToUpper() == "AES") + { + using (var aes = Aes.Create()) + { + var keyBytes = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32)); + aes.Key = keyBytes; + aes.GenerateIV(); + + using (var encryptor = aes.CreateEncryptor()) + using (var ms = new MemoryStream()) + { + ms.Write(aes.IV, 0, aes.IV.Length); + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + { + cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + } + encryptedData = ms.ToArray(); + } + } + } + else + { + throw new EncryptionException($"Unsupported encryption algorithm: {algorithm}"); + } + + SaveAsBinary(encryptedData); + return this; + } + catch (Exception ex) + { + throw new EncryptionException($"Encryption failed: {ex.Message}", ex); + } + } + + // 新增:解密方法(对应 Python 的 decrypt) + public ToolFile Decrypt(string key, string algorithm = "AES") + { + if (!Exists() || !IsFile()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + try + { + byte[] encryptedData = LoadAsBinary(); + byte[] decryptedData; + + if (algorithm.ToUpper() == "AES") + { + using (var aes = Aes.Create()) + { + var keyBytes = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32)); + aes.Key = keyBytes; + + using (var ms = new MemoryStream(encryptedData)) + { + byte[] iv = new byte[16]; + ms.Read(iv, 0, 16); + aes.IV = iv; + + using (var decryptor = aes.CreateDecryptor()) + using (var resultMs = new MemoryStream()) + { + using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) + { + cs.CopyTo(resultMs); + } + decryptedData = resultMs.ToArray(); + } + } + } + } + else + { + throw new EncryptionException($"Unsupported encryption algorithm: {algorithm}"); + } + + SaveAsBinary(decryptedData); + return this; + } + catch (Exception ex) + { + throw new EncryptionException($"Decryption failed: {ex.Message}", ex); + } + } + + #endregion + + #region Hash + + // 新增:计算哈希值(对应 Python 的 calculate_hash) + public string CalculateHash(string algorithm = "MD5", int chunkSize = 8192) + { + if (!Exists() || !IsFile()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + try + { + using (var hashAlgorithm = GetHashAlgorithm(algorithm)) + using (var stream = File.OpenRead(FullPath)) + { + byte[] hash = hashAlgorithm.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLower(); + } + } + catch (Exception ex) + { + throw new HashException($"Hash calculation failed: {ex.Message}", ex); + } + } + + private HashAlgorithm GetHashAlgorithm(string algorithm) + { + return algorithm.ToUpper() switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA512" => SHA512.Create(), + _ => throw new HashException($"Unsupported hash algorithm: {algorithm}") + }; + } + + // 新增:验证哈希值(对应 Python 的 verify_hash) + public bool VerifyHash(string expectedHash, string algorithm = "MD5") + { + string actualHash = CalculateHash(algorithm); + return string.Equals(actualHash, expectedHash, StringComparison.OrdinalIgnoreCase); + } + + // 新增:保存哈希值(对应 Python 的 save_hash) + public ToolFile SaveHash(string algorithm = "MD5", string outputPath = null) + { + string hash = CalculateHash(algorithm); + + if (outputPath == null) + { + outputPath = GetFullPath() + "." + algorithm.ToLower(); + } + + var hashFile = new ToolFile(outputPath); + hashFile.SaveAsText(hash); + + return hashFile; + } + + #endregion + + #region File Monitoring + + // 新增:文件监控(简化实现,对应 Python 的 start_monitoring) + public void StartMonitoring(Action callback, bool recursive = false, + List ignorePatterns = null, bool ignoreDirectories = false, + bool caseSensitive = true, bool isLog = true) + { + if (!IsDir()) + throw new InvalidOperationException("Monitoring is only supported for directories"); + + // 注意:这是一个简化实现,实际的文件监控需要更复杂的实现 + // 可以使用 FileSystemWatcher 来实现完整功能 + var watcher = new FileSystemWatcher(FullPath) + { + IncludeSubdirectories = recursive, + EnableRaisingEvents = true + }; + + watcher.Created += (sender, e) => callback("created", e.FullPath); + watcher.Modified += (sender, e) => callback("modified", e.FullPath); + watcher.Deleted += (sender, e) => callback("deleted", e.FullPath); + watcher.Renamed += (sender, e) => callback("moved", e.FullPath); + } + + #endregion + + #region Backup + + // 新增:创建备份(对应 Python 的 create_backup) + public ToolFile CreateBackup(string backupDir = null, int maxBackups = 5, + string backupFormat = "zip", bool includeMetadata = true) + { + if (!Exists()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + if (backupDir == null) + { + backupDir = Path.Combine(GetDir(), "backups"); + } + + var backupDirectory = new ToolFile(backupDir); + if (!backupDirectory.Exists()) + { + backupDirectory.Create(); + } + + string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + string backupFileName = $"{GetFilename()}_{timestamp}.{backupFormat}"; + string backupPath = Path.Combine(backupDir, backupFileName); + + try + { + if (backupFormat.ToLower() == "zip") + { + Compress(backupPath, "zip"); + } + else + { + Copy(backupPath); + } + + // 清理旧备份 + CleanOldBackups(backupDir, maxBackups, backupFormat); + + return new ToolFile(backupPath); + } + catch (Exception ex) + { + throw new FileOperationException($"Backup creation failed: {ex.Message}", ex); + } + } + + private void CleanOldBackups(string backupDir, int maxBackups, string format) + { + var backupFiles = Directory.GetFiles(backupDir, $"*.{format}") + .Select(f => new FileInfo(f)) + .OrderByDescending(f => f.CreationTime) + .Skip(maxBackups); + + foreach (var file in backupFiles) + { + file.Delete(); + } + } + + // 新增:恢复备份(对应 Python 的 restore_backup) + public ToolFile RestoreBackup(string backupFile, string restorePath = null, bool verifyHash = true) + { + var backupToolFile = new ToolFile(backupFile); + if (!backupToolFile.Exists()) + throw new FileNotFoundException($"Backup file not found: {backupFile}"); + + if (restorePath == null) + { + restorePath = GetFullPath(); + } + + try + { + if (backupToolFile.GetExtension().ToLower() == "zip") + { + backupToolFile.Decompress(restorePath); + } + else + { + backupToolFile.Copy(restorePath); + } + + return new ToolFile(restorePath); + } + catch (Exception ex) + { + throw new FileOperationException($"Backup restoration failed: {ex.Message}", ex); + } + } + + // 新增:列出备份(对应 Python 的 list_backups) + public List ListBackups(string backupDir = null) + { + if (backupDir == null) + { + backupDir = Path.Combine(GetDir(), "backups"); + } + + if (!Directory.Exists(backupDir)) + return new List(); + + return Directory.GetFiles(backupDir) + .Where(f => Path.GetFileName(f).StartsWith(GetFilename())) + .Select(f => new ToolFile(f)) + .OrderByDescending(f => f.GetTimestamp()) + .ToList(); + } + + #endregion + + #region Permissions + + // 新增:获取权限(对应 Python 的 get_permissions) + public Dictionary GetPermissions() + { + if (!Exists()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + var permissions = new Dictionary(); + + try + { + var fileInfo = new FileInfo(FullPath); + var attributes = fileInfo.Attributes; + + permissions["read"] = true; // 如果能获取到文件信息,说明可读 + permissions["write"] = !attributes.HasFlag(FileAttributes.ReadOnly); + permissions["execute"] = false; // Windows 文件没有执行权限概念 + permissions["hidden"] = attributes.HasFlag(FileAttributes.Hidden); + } + catch (Exception) + { + permissions["read"] = false; + permissions["write"] = false; + permissions["execute"] = false; + permissions["hidden"] = false; + } + + return permissions; + } + + // 新增:设置权限(对应 Python 的 set_permissions) + public ToolFile SetPermissions(bool? read = null, bool? write = null, + bool? execute = null, bool? hidden = null, bool recursive = false) + { + if (!Exists()) + throw new FileNotFoundException($"File not found: {GetFullPath()}"); + + try + { + var fileInfo = new FileInfo(FullPath); + var attributes = fileInfo.Attributes; + + if (write.HasValue) + { + if (write.Value) + attributes &= ~FileAttributes.ReadOnly; + else + attributes |= FileAttributes.ReadOnly; + } + + if (hidden.HasValue) + { + if (hidden.Value) + attributes |= FileAttributes.Hidden; + else + attributes &= ~FileAttributes.Hidden; + } + + fileInfo.Attributes = attributes; + + if (recursive && IsDir()) + { + foreach (var child in DirToolFileIter()) + { + child.SetPermissions(read, write, execute, hidden, true); + } + } + + return this; + } + catch (Exception ex) + { + throw new FileOperationException($"Permission setting failed: {ex.Message}", ex); + } + } + + // 新增:权限检查方法(对应 Python 的 is_readable, is_writable 等) + public bool IsReadable() + { + try + { + var permissions = GetPermissions(); + return permissions["read"]; + } + catch + { + return false; + } + } + + public bool IsWritable() + { + try + { + var permissions = GetPermissions(); + return permissions["write"]; + } + catch + { + return false; + } + } + + public bool IsExecutable() + { + try + { + var permissions = GetPermissions(); + return permissions["execute"]; + } + catch + { + return false; + } + } + + public bool IsHidden() + { + try + { + var permissions = GetPermissions(); + return permissions["hidden"]; + } + catch + { + return false; + } + } + + #endregion + + #region Utility Methods + + public ToolFile MakeFileInside(string source, bool isDeleteSource = false) + { + if (IsDir() == false) + throw new InvalidOperationException(); + var sourceFile = new ToolFile(source); + return MakeFileInside(sourceFile, isDeleteSource); + } public static string[] SelectMultipleFiles(string filter = "所有文件|*.*", string title = "选择文件") { - if (PlatformIndicator.IsPlatformWindows) - return WindowsKit.SelectMultipleFiles(filter, title); - throw new NotImplementedException(); + using var dialog = new OpenFileDialog + { + Filter = filter, + Title = title, + Multiselect = true + }; + return dialog.ShowDialog() == DialogResult.OK ? dialog.FileNames : new string[0]; } - public static string SelectFile(string filter = "所有文件|*.*", string title = "选择文件") { - if (PlatformIndicator.IsPlatformWindows) + using var dialog = new OpenFileDialog { - var results = WindowsKit.SelectMultipleFiles(filter, title); - if (results != null && results.Length > 0) - return results[0]; - } - throw new NotImplementedException(); + Filter = filter, + Title = title + }; + return dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null; } - public static string SaveFile(string filter = "所有文件|*.*", string title = "保存文件") { - if (PlatformIndicator.IsPlatformWindows) - return WindowsKit.SaveFile(filter, title); - throw new NotImplementedException(); + using var dialog = new SaveFileDialog + { + Filter = filter, + Title = title + }; + return dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null; } - public static string SelectFolder(string description = "请选择文件夹") { - if (PlatformIndicator.IsPlatformWindows) - return WindowsKit.SelectFolder(description); - throw new NotImplementedException(); + using var dialog = new FolderBrowserDialog + { + Description = description + }; + return dialog.ShowDialog() == DialogResult.OK ? dialog.SelectedPath : null; } - public DateTime GetTimestamp() { - return File.GetLastWriteTime(FullPath).ToUniversalTime(); + return (OriginInfo as FileInfo)?.LastWriteTime ?? DateTime.MinValue; } - public static string BrowseFile(params string[] extensions) { - string filter = ""; - foreach (var ext in extensions) - { - filter += "*." + ext + ";"; - } - string result = SelectFile("所有文件|" + filter); - return result; + string filter = string.Join("|", extensions.Select(ext => $"{ext.ToUpper()} 文件|*.{ext}")); + return SelectFile(filter); } public static ToolFile BrowseToolFile(params string[] extensions) { - return new ToolFile(BrowseFile(extensions)); + string path = BrowseFile(extensions); + return path != null ? new ToolFile(path) : null; } + + #endregion } } diff --git a/Convention/[Runtime]/Math.cs b/Convention/[Runtime]/Math.cs deleted file mode 100644 index fb57512..0000000 --- a/Convention/[Runtime]/Math.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System; - -namespace Convention -{ - -} diff --git a/[Test]/FileTest.cs b/[Test]/FileTest.cs new file mode 100644 index 0000000..f03b2ee --- /dev/null +++ b/[Test]/FileTest.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Convention; + +namespace Convention.Test +{ + public class FileTest + { + public static void RunTests() + { + Console.WriteLine("=== ToolFile 功能测试 ==="); + + // 测试基本文件操作 + TestBasicOperations(); + + // 测试文件加载和保存 + TestLoadAndSave(); + + // 测试压缩和解压 + TestCompression(); + + // 测试加密和解密 + TestEncryption(); + + // 测试哈希计算 + TestHash(); + + // 测试备份功能 + TestBackup(); + + // 测试权限管理 + TestPermissions(); + + Console.WriteLine("=== 所有测试完成 ==="); + } + + private static void TestBasicOperations() + { + Console.WriteLine("\n--- 基本文件操作测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_file.txt"); + testFile.SaveAsText("这是一个测试文件的内容"); + + Console.WriteLine($"文件存在: {testFile.Exists()}"); + Console.WriteLine($"文件大小: {testFile.GetSize()} 字节"); + Console.WriteLine($"文件名: {testFile.GetFilename()}"); + Console.WriteLine($"文件扩展名: {testFile.GetExtension()}"); + Console.WriteLine($"文件目录: {testFile.GetDir()}"); + Console.WriteLine($"文件时间戳: {testFile.GetTimestamp()}"); + + // 清理 + testFile.Remove(); + } + + private static void TestLoadAndSave() + { + Console.WriteLine("\n--- 文件加载和保存测试 ---"); + + // 测试 JSON 保存和加载 + var jsonFile = new ToolFile("test_data.json"); + var testData = new { name = "测试", value = 123, items = new[] { "item1", "item2" } }; + jsonFile.SaveAsRawJson(testData); + + var loadedData = jsonFile.LoadAsRawJson(); + Console.WriteLine($"JSON 数据加载成功: {loadedData != null}"); + + // 测试 CSV 保存和加载 + var csvFile = new ToolFile("test_data.csv"); + var csvData = new List + { + new[] { "姓名", "年龄", "城市" }, + new[] { "张三", "25", "北京" }, + new[] { "李四", "30", "上海" } + }; + csvFile.SaveAsCsv(csvData); + + var loadedCsv = csvFile.LoadAsCsv(); + Console.WriteLine($"CSV 数据加载成功: {loadedCsv.Count} 行"); + + // 清理 + jsonFile.Remove(); + csvFile.Remove(); + } + + private static void TestCompression() + { + Console.WriteLine("\n--- 压缩和解压测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_compress.txt"); + testFile.SaveAsText("这是一个用于压缩测试的文件内容。".PadRight(1000, 'x')); + + // 压缩文件 + var compressedFile = testFile.Compress(); + Console.WriteLine($"压缩成功: {compressedFile.Exists()}"); + Console.WriteLine($"压缩后大小: {compressedFile.GetSize()} 字节"); + + // 解压文件 + var decompressedDir = compressedFile.Decompress(); + Console.WriteLine($"解压成功: {decompressedDir.Exists()}"); + + // 清理 + testFile.Remove(); + compressedFile.Remove(); + if (decompressedDir.Exists()) + decompressedDir.Remove(); + } + + private static void TestEncryption() + { + Console.WriteLine("\n--- 加密和解密测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_encrypt.txt"); + testFile.SaveAsText("这是一个用于加密测试的敏感数据"); + + // 加密文件 + testFile.Encrypt("mysecretkey123"); + Console.WriteLine($"加密成功: {testFile.Exists()}"); + + // 解密文件 + testFile.Decrypt("mysecretkey123"); + var decryptedContent = testFile.LoadAsText(); + Console.WriteLine($"解密成功: {decryptedContent.Contains("敏感数据")}"); + + // 清理 + testFile.Remove(); + } + + private static void TestHash() + { + Console.WriteLine("\n--- 哈希计算测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_hash.txt"); + testFile.SaveAsText("这是一个用于哈希测试的文件内容"); + + // 计算不同算法的哈希值 + var md5Hash = testFile.CalculateHash("MD5"); + var sha256Hash = testFile.CalculateHash("SHA256"); + + Console.WriteLine($"MD5 哈希: {md5Hash}"); + Console.WriteLine($"SHA256 哈希: {sha256Hash}"); + + // 验证哈希值 + var isValid = testFile.VerifyHash(md5Hash, "MD5"); + Console.WriteLine($"哈希验证: {isValid}"); + + // 保存哈希值到文件 + var hashFile = testFile.SaveHash("MD5"); + Console.WriteLine($"哈希文件保存: {hashFile.Exists()}"); + + // 清理 + testFile.Remove(); + hashFile.Remove(); + } + + private static void TestBackup() + { + Console.WriteLine("\n--- 备份功能测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_backup.txt"); + testFile.SaveAsText("这是一个用于备份测试的文件内容"); + + // 创建备份 + var backupFile = testFile.CreateBackup(); + Console.WriteLine($"备份创建成功: {backupFile.Exists()}"); + + // 列出备份 + var backups = testFile.ListBackups(); + Console.WriteLine($"备份数量: {backups.Count}"); + + // 恢复备份 + var restoredFile = testFile.RestoreBackup(backupFile.GetFullPath(), "restored_file.txt"); + Console.WriteLine($"备份恢复成功: {restoredFile.Exists()}"); + + // 清理 + testFile.Remove(); + backupFile.Remove(); + restoredFile.Remove(); + } + + private static void TestPermissions() + { + Console.WriteLine("\n--- 权限管理测试 ---"); + + // 创建测试文件 + var testFile = new ToolFile("test_permissions.txt"); + testFile.SaveAsText("这是一个用于权限测试的文件内容"); + + // 获取权限 + var permissions = testFile.GetPermissions(); + Console.WriteLine($"读取权限: {permissions["read"]}"); + Console.WriteLine($"写入权限: {permissions["write"]}"); + Console.WriteLine($"隐藏属性: {permissions["hidden"]}"); + + // 设置权限 + testFile.SetPermissions(hidden: true); + var newPermissions = testFile.GetPermissions(); + Console.WriteLine($"设置隐藏后: {newPermissions["hidden"]}"); + + // 检查权限 + Console.WriteLine($"可读: {testFile.IsReadable()}"); + Console.WriteLine($"可写: {testFile.IsWritable()}"); + Console.WriteLine($"隐藏: {testFile.IsHidden()}"); + + // 清理 + testFile.Remove(); + } + } +} \ No newline at end of file diff --git a/[Test]/Program.cs b/[Test]/Program.cs index 3e72f3a..c3e1d5d 100644 --- a/[Test]/Program.cs +++ b/[Test]/Program.cs @@ -5,20 +5,19 @@ using System.Threading; using Convention; using Convention.EasySave; using Convention.Symbolization; +using Convention.Test; public class Program { static void Main(string[] args) { - var runner = new SymbolizationRunner(); - try - { - runner.Compile("example_script.txt"); - Console.WriteLine("Script compiled successfully."); - } - catch (FileNotFoundException ex) - { - Console.WriteLine($"Error: {ex.Message}"); - } + Console.WriteLine("Convention-CSharp 测试程序"); + Console.WriteLine("=========================="); + + // 运行文件功能测试 + FileTest.RunTests(); + + Console.WriteLine("\n按任意键退出..."); + Console.ReadKey(); } } \ No newline at end of file