From ca2d7f49e116f8f636edfa2889035e1a10f1fca2 Mon Sep 17 00:00:00 2001 From: Ling0925 <2449858657a@gmail.com> Date: Sun, 22 Jun 2025 21:16:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=BE=93=E5=85=A5=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=92=8C=E6=96=87=E4=BB=B6=E4=BF=9D=E5=AD=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `Scanner.cs` 中添加了对 `System.Diagnostics` 的引用,并优化了 `MoveNext` 方法以简化空白字符处理和错误处理逻辑。 在 `StringNbt.cs` 中引入了 `System.IO` 和 `System.Numerics`,并修改了解析逻辑以增强输入处理和错误检查。新增了 `IsLineEnd` 方法,并简化了 `MoveNext` 的调用。添加了 `SaveToFile` 和 `SaveToStream` 方法,以支持将 `CompoundTag` 保存为 SNBT 格式。 --- SharpNBT/SNBT/Scanner.cs | 21 ++++++-- SharpNBT/SNBT/StringNbt.cs | 106 ++++++++++++++++++++++++++++++------- 2 files changed, 105 insertions(+), 22 deletions(-) diff --git a/SharpNBT/SNBT/Scanner.cs b/SharpNBT/SNBT/Scanner.cs index 0da765b..2d67d81 100644 --- a/SharpNBT/SNBT/Scanner.cs +++ b/SharpNBT/SNBT/Scanner.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -44,12 +45,26 @@ internal ref struct Scanner SyntaxError("Unexpected end of input."); return false; } - - if (skipWhitespace && char.IsWhiteSpace(Current)) + //Debug.Write(Current); + if (skipWhitespace && string.IsNullOrWhiteSpace(Current.ToString())) + goto ReadChar; + return true; + } + public bool MoveNext(bool fail) + { + ReadChar: + Position++; + if (Position >= Source.Length) + { + if (fail) + SyntaxError("Unexpected end of input."); + return false; + } + //Debug.Write(Current); + if (char.IsWhiteSpace(Current) && Current != '\n') goto ReadChar; return true; } - public void AssertChar(char c) { if (Current != c) diff --git a/SharpNBT/SNBT/StringNbt.cs b/SharpNBT/SNBT/StringNbt.cs index b9b2383..282125b 100644 --- a/SharpNBT/SNBT/StringNbt.cs +++ b/SharpNBT/SNBT/StringNbt.cs @@ -1,10 +1,11 @@ +using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Data; using System.Globalization; +using System.IO; using System.Numerics; using System.Text; -using JetBrains.Annotations; namespace SharpNBT.SNBT; @@ -83,6 +84,12 @@ public static class StringNbt while (!scanner.IsEndOfInput) { + // Closing brace encountered, break loop. + if (scanner.Current == '}') + { + // scanner.MoveNext(true, false); + break; + } // Read the name of the tag var childName = ParseString(ref scanner, out _); @@ -94,22 +101,22 @@ public static class StringNbt scanner.MoveNext(true, true); var tag = ParseTag(childName, ref scanner); result.Add(tag); - scanner.MoveNext(true, true); + scanner.MoveNext( true); // Comma encountered, read another tag. - if (scanner.Current == ',') + if (IsLineEnd(scanner.Current)) { scanner.MoveNext(true, true); continue; } - // Closing brace encountered, break loop. if (scanner.Current == '}') { // scanner.MoveNext(true, false); break; } - + + // Invalid character scanner.SyntaxError($"Expected ',' or '}}', got '{scanner.Current}'."); } @@ -309,24 +316,28 @@ public static class StringNbt var list = new List(); while (true) { - var child = ParseTag(null, ref scanner); - list.Add(child); - - scanner.MoveNext(true, true); - - // Comma encountered, read another tag. - if (scanner.Current == ',') - { - scanner.MoveNext(true, true); - continue; - } - // Closing brace encountered, break loop. if (scanner.Current == ']') { break; } + var child = ParseTag(null, ref scanner); + list.Add(child); + scanner.MoveNext(true); + + // Comma encountered, read another tag. + if (IsLineEnd(scanner.Current)) + { + scanner.MoveNext(true, true); + continue; + } + + if (scanner.Current == ']') + { + break; + } + // Invalid character scanner.SyntaxError($"Expected ',' or ']', got '{scanner.Current}'."); } @@ -349,7 +360,7 @@ public static class StringNbt var c = char.ToLowerInvariant(scanner.Current); if (c == ']') break; - if (char.IsNumber(c) || c == ',') + if (char.IsNumber(c) ||c == ',' || c == '-') continue; if (c is not ('b' or 'l')) scanner.SyntaxError($"Invalid character '{c}' in integer array."); @@ -366,5 +377,62 @@ public static class StringNbt } private const StringSplitOptions SplitOpts = StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries; - private static readonly char[] SplitSeparators = new[] { ',', 'b', 'B', 'l', 'L' }; + private static readonly char[] SplitSeparators = new[] { ',', 'b', 'B', 'l', 'L','\n' }; + + private static bool IsLineEnd(char c) + { + return c == ',' || c == '\n' || c == '\r'; + } + + + /// + /// Saves a to a file in SNBT format. + /// + /// The to save. + /// The path to the file where the SNBT data will be saved. + /// Whether to format the output in a human-readable format. + /// When is . + /// When there is an error writing to the file. + public static void SaveToFile(CompoundTag tag, string filePath, bool prettyPrint = false) + { + if (tag == null) + throw new ArgumentNullException(nameof(tag)); + + string snbt = prettyPrint ? tag.PrettyPrinted() : tag.Stringify(true); + + try + { + File.WriteAllText(filePath, snbt, Encoding.UTF8); + } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException) + { + throw new IOException($"Failed to write SNBT to file '{filePath}': {ex.Message}", ex); + } + } + + /// + /// Saves a to a file in SNBT format. + /// + /// The to save. + /// The stream where the SNBT data will be written. + /// Whether to format the output in a human-readable format. + /// The text encoding to use. Defaults to UTF-8 if null. + /// When or is . + /// When there is an error writing to the stream. + public static void SaveToStream(CompoundTag tag, Stream stream, bool prettyPrint = false, Encoding? encoding = null) + { + if (tag == null) + throw new ArgumentNullException(nameof(tag)); + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (!stream.CanWrite) + throw new IOException("Cannot write to the specified stream."); + + string snbt = prettyPrint ? tag.PrettyPrinted() : tag.Stringify(true); + encoding ??= Encoding.UTF8; + + using var writer = new StreamWriter(stream, encoding, leaveOpen: true); + writer.Write(snbt); + writer.Flush(); + } } \ No newline at end of file