Prechádzať zdrojové kódy

文本对比下放到.NET 4.6.1可用

懒得勤快 1 rok pred
rodič
commit
fc0866434f

+ 1 - 1
Directory.Build.props

@@ -1,6 +1,6 @@
 <Project>
  <PropertyGroup>
-   <Version>2024.5.1</Version>
+   <Version>2024.5.2</Version>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
 </Project>

+ 9 - 0
Masuit.Tools.Abstractions/Extensions/BaseType/IEnumerableExtensions.cs

@@ -14,6 +14,15 @@ namespace Masuit.Tools;
 /// </summary>
 public static class IEnumerableExtensions
 {
+#if NETFRAMEWORK
+
+	public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item)
+	{
+		return source.Concat([item]);
+	}
+
+#endif
+
 	/// <summary>
 	/// 按字段属性判等取交集
 	/// </summary>

+ 11 - 0
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -2,6 +2,7 @@
     <PropertyGroup>
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7;net8</TargetFrameworks>
         <LangVersion>latest</LangVersion>
+        <ImplicitUsings>enable</ImplicitUsings>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
         <Authors>懒得勤快</Authors>
         <Description>全龄段友好的C#万能工具库,码数吐司库,不管你是菜鸟新手还是骨灰级玩家都能轻松上手,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
@@ -61,12 +62,18 @@
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
+        <PackageReference Include="IndexRange" Version="1.0.3" />
+        <PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
+        <PackageReference Include="System.Memory" Version="4.5.5" />
         <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="[1.0.0]" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1'">
+        <PackageReference Include="IndexRange" Version="1.0.3" />
+        <PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
+        <PackageReference Include="System.Memory" Version="4.5.5" />
         <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="[1.0.0]" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
@@ -94,6 +101,10 @@
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
         <Reference Include="System.Web" />
+        <PackageReference Include="Castle.Core" Version="[4.4.1]" />
+        <PackageReference Include="IndexRange" Version="1.0.3" />
+        <PackageReference Include="System.Collections.Immutable" Version="[6.0.0]" />
+        <PackageReference Include="System.Memory" Version="4.5.5" />
         <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="[1.0.0]" />
         <PackageReference Include="System.Buffers" version="[4.5.1]" targetFramework="net461" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />

+ 119 - 121
Masuit.Tools.Abstractions/Strings/ValidateCode.cs

@@ -1,15 +1,13 @@
-using System;
-using System.Linq;
-using System.Security.Cryptography;
+using System.Security.Cryptography;
 using System.Text;
 using Masuit.Tools.Systems;
 using SixLabors.Fonts;
 using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Drawing;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing;
 using SixLabors.ImageSharp.Drawing.Processing;
 using SixLabors.ImageSharp.Formats.Webp;
+using Path = SixLabors.ImageSharp.Drawing.Path;
 
 namespace Masuit.Tools.Strings;
 
@@ -18,120 +16,120 @@ namespace Masuit.Tools.Strings;
 /// </summary>
 public static class ValidateCode
 {
-    /// <summary>
-    /// 生成验证码
-    /// </summary>
-    /// <param name="length">指定验证码的长度</param>
-    /// <returns>验证码字符串</returns>
-    public static string CreateValidateCode(int length)
-    {
-        string ch = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890@#$%&?";
-        byte[] b = new byte[4];
-        using var cpt = RandomNumberGenerator.Create();
-        cpt.GetBytes(b);
-        var r = new Random(BitConverter.ToInt32(b, 0));
-        var sb = new StringBuilder();
-        for (int i = 0; i < length; i++)
-        {
-            sb.Append(ch[r.StrictNext(ch.Length)]);
-        }
-
-        return sb.ToString();
-    }
-
-    /// <summary>
-    /// 创建验证码的图片
-    /// </summary>
-    /// <param name="validateCode">验证码序列</param>
-    /// <param name="fontSize">字体大小,默认值28px</param>
-    /// <exception cref="Exception">The operation failed.</exception>
-    public static PooledMemoryStream CreateValidateGraphic(this string validateCode, int fontSize = 28)
-    {
-        var fontFamily = SystemFonts.Families.Where(f => new[] { "Consolas", "DejaVu Sans", "KaiTi", "NSimSun", "SimSun", "SimHei", "Microsoft YaHei UI", "Arial" }.Contains(f.Name)).OrderByRandom().FirstOrDefault();
-        if (fontFamily == default)
-        {
-            fontFamily = SystemFonts.Families.OrderByRandom().FirstOrDefault();
-        }
-
-        var font = fontFamily.CreateFont(fontSize);
-        var measure = TextMeasurer.MeasureSize(validateCode, new TextOptions(font));
-        var width = (int)Math.Ceiling(measure.Width + 15);
-        var height = (int)Math.Ceiling(measure.Height + 15);
-        using var image = new Image<Rgba32>(width, height);
-
-        //生成随机生成器
-        Random random = new Random();
-
-        //清空图片背景色
-        image.Mutate(g =>
-        {
-            g.BackgroundColor(Color.White);
-
-            //画图片的干扰线
-            for (int i = 0; i < 65; i++)
-            {
-                int x1 = random.StrictNext(width);
-                int x2 = random.StrictNext(width);
-                int y1 = random.StrictNext(height);
-                int y2 = random.StrictNext(height);
-                g.Draw(new SolidPen(new Color(new Rgba32((byte)random.StrictNext(255), (byte)random.StrictNext(255), (byte)random.StrictNext(255))), 1), new Path(new[] { new PointF(x1, y1), new PointF(x2, y2) }));
-            }
-
-            //渐变.
-            var brush = new LinearGradientBrush(new PointF(0, 0), new PointF(width, height), GradientRepetitionMode.Repeat, new ColorStop(0.5f, Color.Blue), new ColorStop(0.5f, Color.DarkRed));
-            g.DrawText(validateCode, font, brush, new PointF(3, 2));
-
-            //画图片的边框线
-            g.Draw(Color.Silver, 1, new RectangleF(0, 0, width - 1, height - 1));
-        });
-
-        //画图片的前景干扰点
-        for (int i = 0; i < 350; i++)
-        {
-            int x = random.StrictNext(image.Width);
-            int y = random.StrictNext(image.Height);
-            image[x, y] = new Rgba32(random.StrictNext(255), random.StrictNext(255), random.StrictNext(255));
-        }
-
-        //保存图片数据
-        var stream = new PooledMemoryStream();
-        image.Save(stream, WebpFormat.Instance);
-        stream.Position = 0;
-        return stream;
-    }
-
-    /// <summary>
-    /// 字符串宽度
-    /// </summary>
-    /// <param name="s"></param>
-    /// <param name="fontSize"></param>
-    /// <returns></returns>
-    public static float StringWidth(this string s, int fontSize = 1)
-    {
-        var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == "Microsoft YaHei UI");
-        if (fontFamily == default)
-        {
-            fontFamily = SystemFonts.Families.FirstOrDefault();
-        }
-
-        return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
-    }
-
-    /// <summary>
-    /// 字符串宽度
-    /// </summary>
-    /// <param name="s"></param>
-    /// <param name="fontName">字体名字,如:Microsoft YaHei UI</param>
-    /// <param name="fontSize"></param>
-    /// <returns></returns>
-    public static float StringWidth(this string s, string fontName, int fontSize = 1)
-    {
-        var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == fontName);
-        if (fontFamily == default)
-        {
-            throw new ArgumentException($"字体 {fontName} 不存在,请尝试其它字体!");
-        }
-
-        return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
-    }
-}
+	/// <summary>
+	/// 生成验证码
+	/// </summary>
+	/// <param name="length">指定验证码的长度</param>
+	/// <returns>验证码字符串</returns>
+	public static string CreateValidateCode(int length)
+	{
+		string ch = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890@#$%&?";
+		byte[] b = new byte[4];
+		using var cpt = RandomNumberGenerator.Create();
+		cpt.GetBytes(b);
+		var r = new Random(BitConverter.ToInt32(b, 0));
+		var sb = new StringBuilder();
+		for (int i = 0; i < length; i++)
+		{
+			sb.Append(ch[r.StrictNext(ch.Length)]);
+		}
+
+		return sb.ToString();
+	}
+
+	/// <summary>
+	/// 创建验证码的图片
+	/// </summary>
+	/// <param name="validateCode">验证码序列</param>
+	/// <param name="fontSize">字体大小,默认值28px</param>
+	/// <exception cref="Exception">The operation failed.</exception>
+	public static PooledMemoryStream CreateValidateGraphic(this string validateCode, int fontSize = 28)
+	{
+		var fontFamily = SystemFonts.Families.Where(f => new[] { "Consolas", "DejaVu Sans", "KaiTi", "NSimSun", "SimSun", "SimHei", "Microsoft YaHei UI", "Arial" }.Contains(f.Name)).OrderByRandom().FirstOrDefault();
+		if (fontFamily == default)
+		{
+			fontFamily = SystemFonts.Families.OrderByRandom().FirstOrDefault();
+		}
+
+		var font = fontFamily.CreateFont(fontSize);
+		var measure = TextMeasurer.MeasureSize(validateCode, new TextOptions(font));
+		var width = (int)Math.Ceiling(measure.Width + 15);
+		var height = (int)Math.Ceiling(measure.Height + 15);
+		using var image = new Image<Rgba32>(width, height);
+
+		//生成随机生成器
+		Random random = new Random();
+
+		//清空图片背景色
+		image.Mutate(g =>
+		{
+			g.BackgroundColor(Color.White);
+
+			//画图片的干扰线
+			for (int i = 0; i < 65; i++)
+			{
+				int x1 = random.StrictNext(width);
+				int x2 = random.StrictNext(width);
+				int y1 = random.StrictNext(height);
+				int y2 = random.StrictNext(height);
+				g.Draw(new SolidPen(new Color(new Rgba32((byte)random.StrictNext(255), (byte)random.StrictNext(255), (byte)random.StrictNext(255))), 1), new Path(new[] { new PointF(x1, y1), new PointF(x2, y2) }));
+			}
+
+			//渐变.
+			var brush = new LinearGradientBrush(new PointF(0, 0), new PointF(width, height), GradientRepetitionMode.Repeat, new ColorStop(0.5f, Color.Blue), new ColorStop(0.5f, Color.DarkRed));
+			g.DrawText(validateCode, font, brush, new PointF(3, 2));
+
+			//画图片的边框线
+			g.Draw(Color.Silver, 1, new RectangleF(0, 0, width - 1, height - 1));
+		});
+
+		//画图片的前景干扰点
+		for (int i = 0; i < 350; i++)
+		{
+			int x = random.StrictNext(image.Width);
+			int y = random.StrictNext(image.Height);
+			image[x, y] = new Rgba32(random.StrictNext(255), random.StrictNext(255), random.StrictNext(255));
+		}
+
+		//保存图片数据
+		var stream = new PooledMemoryStream();
+		image.Save(stream, WebpFormat.Instance);
+		stream.Position = 0;
+		return stream;
+	}
+
+	/// <summary>
+	/// 字符串宽度
+	/// </summary>
+	/// <param name="s"></param>
+	/// <param name="fontSize"></param>
+	/// <returns></returns>
+	public static float StringWidth(this string s, int fontSize = 1)
+	{
+		var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == "Microsoft YaHei UI");
+		if (fontFamily == default)
+		{
+			fontFamily = SystemFonts.Families.FirstOrDefault();
+		}
+
+		return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
+	}
+
+	/// <summary>
+	/// 字符串宽度
+	/// </summary>
+	/// <param name="s"></param>
+	/// <param name="fontName">字体名字,如:Microsoft YaHei UI</param>
+	/// <param name="fontSize"></param>
+	/// <returns></returns>
+	public static float StringWidth(this string s, string fontName, int fontSize = 1)
+	{
+		var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == fontName);
+		if (fontFamily == default)
+		{
+			throw new ArgumentException($"字体 {fontName} 不存在,请尝试其它字体!");
+		}
+
+		return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
+	}
+}

+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/BitapAlgorithm.cs → Masuit.Tools.Abstractions/TextDiff/BitapAlgorithm.cs


+ 17 - 0
Masuit.Tools.AspNetCore/TextDiff/DiffAlgorithm.cs → Masuit.Tools.Abstractions/TextDiff/DiffAlgorithm.cs

@@ -102,9 +102,16 @@ internal static class DiffAlgorithm
 			var result = TextUtil.HalfMatch(text1, text2);
 			if (!result.IsEmpty)
 			{
+#if NETSTANDARD2_1_OR_GREATER
 				var diffsA = Compute(result.Prefix1, result.Prefix2, checklines, true, token);
 				var diffsB = Compute(result.Suffix1, result.Suffix2, checklines, true, token);
 				return diffsA.Concat(TextDiffer.Equal(result.CommonMiddle)).Concat(diffsB);
+#else
+				var diffsA = Compute(result.Prefix1.AsSpan(), result.Prefix2.AsSpan(), checklines, true, token);
+				var diffsB = Compute(result.Suffix1.AsSpan(), result.Suffix2.AsSpan(), checklines, true, token);
+				return diffsA.Concat(TextDiffer.Equal(result.CommonMiddle.AsSpan())).Concat(diffsB);
+
+#endif
 			}
 		}
 
@@ -127,9 +134,13 @@ internal static class DiffAlgorithm
 	private static List<TextDiffer> DiffLines(ReadOnlySpan<char> text1, ReadOnlySpan<char> text2, bool optimized, CancellationToken token)
 	{
 		var compressor = new StringCompressor();
+#if NETSTANDARD2_1_OR_GREATER
 		text1 = compressor.Compress(text1, char.MaxValue * 2 / 3);
 		text2 = compressor.Compress(text2);
 		var diffs = Compute(text1, text2, false, optimized, token).Select(diff => diff.Replace(compressor.Decompress(diff.Text))).ToList().CleanupSemantic();
+#else
+		var diffs = Compute(compressor.Compress(text1, char.MaxValue * 2 / 3).AsSpan(), compressor.Compress(text2).AsSpan(), false, optimized, token).Select(diff => diff.Replace(compressor.Decompress(diff.Text))).ToList().CleanupSemantic();
+#endif
 
 		return RediffAfterDiffLines(diffs, optimized, token).ToList();
 	}
@@ -161,9 +172,15 @@ internal static class DiffAlgorithm
 
 			var consolidatedDiffsBeforeEqual = diff.Operation switch
 			{
+#if NETSTANDARD2_1_OR_GREATER
 				Equal when ins.Length > 0 && del.Length > 0 => Compute(del.ToString(), ins.ToString(), false, optimized, token),
 				Equal when del.Length > 0 => TextDiffer.Delete(del.ToString()).ItemAsEnumerable(),
 				Equal when ins.Length > 0 => TextDiffer.Insert(ins.ToString()).ItemAsEnumerable(),
+#else
+				Equal when ins.Length > 0 && del.Length > 0 => Compute(del.ToString().AsSpan(), ins.ToString().AsSpan(), false, optimized, token),
+				Equal when del.Length > 0 => TextDiffer.Delete(del.ToString().AsSpan()).ItemAsEnumerable(),
+				Equal when ins.Length > 0 => TextDiffer.Insert(ins.ToString().AsSpan()).ItemAsEnumerable(),
+#endif
 				_ => []
 			};
 

+ 28 - 3
Masuit.Tools.AspNetCore/TextDiff/DiffExtension.cs → Masuit.Tools.Abstractions/TextDiff/DiffExtension.cs

@@ -1,4 +1,6 @@
-using System.Collections.Immutable;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Text;
 using System.Text.RegularExpressions;
 using static Masuit.Tools.TextDiff.DiffOperation;
@@ -175,8 +177,13 @@ public static class DiffExtension
 						yield return lastEquality;
 					}
 
-					if (sbDelete.Length > 0) yield return TextDiffer.Delete(sbDelete.ToString());
-					if (sbInsert.Length > 0) yield return TextDiffer.Insert(sbInsert.ToString());
+#if NETSTANDARD2_1_OR_GREATER
+                    if (sbDelete.Length > 0) yield return TextDiffer.Delete(sbDelete.ToString());
+                    if (sbInsert.Length > 0) yield return TextDiffer.Insert(sbInsert.ToString());
+#else
+					if (sbDelete.Length > 0) yield return TextDiffer.Delete(sbDelete.ToString().AsSpan());
+					if (sbInsert.Length > 0) yield return TextDiffer.Insert(sbInsert.ToString().AsSpan());
+#endif
 					lastEquality = diff;
 					sbDelete.Clear();
 					sbInsert.Clear();
@@ -269,7 +276,11 @@ public static class DiffExtension
 		// 将编辑尽可能向左移动
 		public EditBetweenEqualities ShiftLeft()
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			var commonOffset = TextUtil.CommonSuffix(Equality1, Edit);
+#else
+			var commonOffset = TextUtil.CommonSuffix(Equality1.AsSpan(), Edit.AsSpan());
+#endif
 			if (commonOffset > 0)
 			{
 				var commonString = Edit[^commonOffset..];
@@ -295,9 +306,15 @@ public static class DiffExtension
 
 		public IEnumerable<TextDiffer> ToDiffs(DiffOperation edit)
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			yield return TextDiffer.Equal(Equality1);
 			yield return TextDiffer.Create(edit, Edit);
 			yield return TextDiffer.Equal(Equality2);
+#else
+			yield return TextDiffer.Equal(Equality1.AsSpan());
+			yield return TextDiffer.Create(edit, Edit);
+			yield return TextDiffer.Equal(Equality2.AsSpan());
+#endif
 		}
 	}
 
@@ -426,7 +443,11 @@ public static class DiffExtension
                  */
 				if ((lastEquality.Length != 0) && ((insertionBeforeLastEquality && deletionBeforeLastEquality && insertionAfterLastEquality && deletionAfterLastEquality) || ((lastEquality.Length < diffEditCost / 2) && (insertionBeforeLastEquality ? 1 : 0) + (deletionBeforeLastEquality ? 1 : 0) + (insertionAfterLastEquality ? 1 : 0) + (deletionAfterLastEquality ? 1 : 0) == 3)))
 				{
+#if NETSTANDARD2_1_OR_GREATER
 					diffs.Splice(equalities.Peek(), 1, TextDiffer.Delete(lastEquality), TextDiffer.Insert(lastEquality));
+#else
+					diffs.Splice(equalities.Peek(), 1, TextDiffer.Delete(lastEquality.AsSpan()), TextDiffer.Insert(lastEquality.AsSpan()));
+#endif
 					equalities.Pop();
 					lastEquality = string.Empty;
 					if (insertionBeforeLastEquality && deletionBeforeLastEquality)
@@ -513,7 +534,11 @@ public static class DiffExtension
 
 				if (lastEquality != null && (lastEquality.Length <= Math.Max(lengthInsertions1, lengthDeletions1)) && (lastEquality.Length <= Math.Max(lengthInsertions2, lengthDeletions2)))
 				{
+#if NETSTANDARD2_1_OR_GREATER
 					diffs.Splice(equalities.Peek(), 1, TextDiffer.Delete(lastEquality), TextDiffer.Insert(lastEquality));
+#else
+					diffs.Splice(equalities.Peek(), 1, TextDiffer.Delete(lastEquality.AsSpan()), TextDiffer.Insert(lastEquality.AsSpan()));
+#endif
 					equalities.Pop();
 					if (equalities.Count > 0)
 					{

+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/DiffOperation.cs → Masuit.Tools.Abstractions/TextDiff/DiffOperation.cs


+ 12 - 0
Masuit.Tools.AspNetCore/TextDiff/DiffPatch.cs → Masuit.Tools.Abstractions/TextDiff/DiffPatch.cs

@@ -39,7 +39,11 @@ public record DiffPatch(int Start1, int Length1, int Start2, int Length2, Semant
 	{
 		if (!StartsWith(Equal))
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			builder.Insert(0, TextDiffer.Equal(padding));
+#else
+			builder.Insert(0, TextDiffer.Equal(padding.AsSpan()));
+#endif
 			return (s1 - padding.Length, l1 + padding.Length, s2 - padding.Length, l2 + padding.Length);
 		}
 
@@ -68,7 +72,11 @@ public record DiffPatch(int Start1, int Length1, int Start2, int Length2, Semant
 	{
 		if (!EndsWith(Equal))
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			builder.Add(TextDiffer.Equal(padding));
+#else
+			builder.Add(TextDiffer.Equal(padding.AsSpan()));
+#endif
 			return (s1, l1 + padding.Length, s2, l2 + padding.Length);
 		}
 
@@ -96,7 +104,11 @@ public record DiffPatch(int Start1, int Length1, int Start2, int Length2, Semant
 	public static SemanticsImmutableList<DiffPatch> Compute(string text1, string text2, float diffTimeout = 0, short diffEditCost = 4)
 	{
 		using var cts = diffTimeout <= 0 ? new CancellationTokenSource() : new CancellationTokenSource(TimeSpan.FromSeconds(diffTimeout));
+#if NETSTANDARD2_1_OR_GREATER
 		return Compute(text1, DiffAlgorithm.Compute(text1, text2, true, true, cts.Token).CleanupSemantic().CleanupEfficiency(diffEditCost)).ToImmutableList().WithValueSemantics();
+#else
+		return Compute(text1, DiffAlgorithm.Compute(text1.AsSpan(), text2.AsSpan(), true, true, cts.Token).CleanupSemantic().CleanupEfficiency(diffEditCost)).ToImmutableList().WithValueSemantics();
+#endif
 	}
 
 	/// <summary>

+ 8 - 0
Masuit.Tools.AspNetCore/TextDiff/DifferBuilder.cs → Masuit.Tools.Abstractions/TextDiff/DifferBuilder.cs

@@ -35,7 +35,11 @@ internal static class DifferBuilder
 		var prefix = text[begin1..start2];
 		if (prefix.Length != 0)
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			diffListBuilder.Insert(0, TextDiffer.Equal(prefix));
+#else
+			diffListBuilder.Insert(0, TextDiffer.Equal(prefix.AsSpan()));
+#endif
 		}
 
 		var begin2 = start2 + length1;
@@ -43,7 +47,11 @@ internal static class DifferBuilder
 		var suffix = text.Substring(begin2, length);
 		if (suffix.Length != 0)
 		{
+#if NETSTANDARD2_1_OR_GREATER
 			diffListBuilder.Add(TextDiffer.Equal(suffix));
+#else
+			diffListBuilder.Add(TextDiffer.Equal(suffix.AsSpan()));
+#endif
 		}
 
 		start1 -= prefix.Length;

+ 15 - 5
Masuit.Tools.AspNetCore/TextDiff/Extensions.cs → Masuit.Tools.Abstractions/TextDiff/Extensions.cs

@@ -61,14 +61,19 @@ public static partial class Extensions
 
 		var regex = new Regex(@"<pre[\s\S]*?</pre>|<[^>]+>");
 		const string sep = "\f";
+#if NETSTANDARD2_1_OR_GREATER
 		var tags1 = regex.Matches(text1).Select(m => m.Value).Append("").ToArray();
 		var tags2 = regex.Matches(text2).Select(m => m.Value).Append("").ToArray();
+#else
+		var tags1 = regex.Matches(text1).Cast<Match>().Select(m => m.Value).Append("").ToArray();
+		var tags2 = regex.Matches(text2).Cast<Match>().Select(m => m.Value).Append("").ToArray();
+#endif
 		var html1 = regex.Replace(text1, sep);
 		var html2 = regex.Replace(text2, sep);
 		var diffs = TextDiffer.Compute(html1, html2);
-		var s1 = diffs.Where(d => d.Operation != DiffOperation.Insert).Select(diff => diff.Operation == DiffOperation.Equal || string.IsNullOrWhiteSpace(diff.Text) ? diff.Text : diff.Text.Split(sep).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<del>{s}</del>").Join(sep)).Join("");
-		var s2 = diffs.Where(d => d.Operation != DiffOperation.Delete).Select(diff => diff.Operation == DiffOperation.Equal || string.IsNullOrWhiteSpace(diff.Text) ? diff.Text : diff.Text.Split(sep).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<ins>{s}</ins>").Join(sep)).Join("");
-		return (s1.Split(sep).Select((s, i) => s + tags1[i]).Join(""), s2.Split(sep).Select((s, i) => s + tags2[i]).Join(""));
+		var s1 = diffs.Where(d => d.Operation != DiffOperation.Insert).Select(diff => diff.Operation == DiffOperation.Equal || string.IsNullOrWhiteSpace(diff.Text) ? diff.Text : diff.Text.Split(sep[0]).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<del>{s}</del>").Join(sep)).Join("");
+		var s2 = diffs.Where(d => d.Operation != DiffOperation.Delete).Select(diff => diff.Operation == DiffOperation.Equal || string.IsNullOrWhiteSpace(diff.Text) ? diff.Text : diff.Text.Split(sep[0]).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<ins>{s}</ins>").Join(sep)).Join("");
+		return (s1.Split(sep[0]).Select((s, i) => s + tags1[i]).Join(""), s2.Split(sep[0]).Select((s, i) => s + tags2[i]).Join(""));
 	}
 
 	public static string HtmlDiffMerge(this string text1, string text2)
@@ -85,8 +90,13 @@ public static partial class Extensions
 
 		var regex = new Regex(@"<pre[\s\S]*?</pre>|<[^>]+>");
 		const string sep = "\f";
+#if NETSTANDARD2_1_OR_GREATER
 		var tags1 = regex.Matches(text1).Select(m => m.Value).Append("").ToQueue();
 		var tags2 = regex.Matches(text2).Select(m => m.Value).Append("").ToQueue();
+#else
+		var tags1 = regex.Matches(text1).Cast<Match>().Select(m => m.Value).Append("").ToQueue();
+		var tags2 = regex.Matches(text2).Cast<Match>().Select(m => m.Value).Append("").ToQueue();
+#endif
 		var html1 = regex.Replace(text1, sep);
 		var html2 = regex.Replace(text2, sep);
 		var diffs = TextDiffer.Compute(html1, html2);
@@ -109,7 +119,7 @@ public static partial class Extensions
 
 				case DiffOperation.Delete:
 					{
-						var str = diff.Text.Split(sep).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<del>{s}</del>").Join(sep);
+						var str = diff.Text.Split(sep[0]).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<del>{s}</del>").Join(sep);
 						foreach (Match m in Regex.Matches(str, sep))
 						{
 							var tag = tags1.Dequeue();
@@ -121,7 +131,7 @@ public static partial class Extensions
 
 				case DiffOperation.Insert:
 					{
-						var str = diff.Text.Split(sep).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<ins>{s}</ins>").Join(sep);
+						var str = diff.Text.Split(sep[0]).Select(s => string.IsNullOrWhiteSpace(s) ? s : $"<ins>{s}</ins>").Join(sep);
 						foreach (Match m in Regex.Matches(str, sep))
 						{
 							var tag = tags2.Dequeue();

+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/HalfMatchResult.cs → Masuit.Tools.Abstractions/TextDiff/HalfMatchResult.cs


+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/IsExternalInit.cs → Masuit.Tools.Abstractions/TextDiff/IsExternalInit.cs


+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/MatchOption.cs → Masuit.Tools.Abstractions/TextDiff/MatchOption.cs


+ 22 - 0
Masuit.Tools.AspNetCore/TextDiff/PatchExtension.cs → Masuit.Tools.Abstractions/TextDiff/PatchExtension.cs

@@ -101,9 +101,15 @@ public static class PatchExtension
 
 						yield return sign switch
 						{
+#if NETSTANDARD2_1_OR_GREATER
 							'+' => TextDiffer.Insert(line[1..].Replace("+", "%2b").UrlDecoded()),
 							'-' => TextDiffer.Delete(line[1..].Replace("+", "%2b").UrlDecoded()),
 							_ => TextDiffer.Equal(line[1..].Replace("+", "%2b").UrlDecoded())
+#else
+							'+' => TextDiffer.Insert(line[1..].Replace("+", "%2b").UrlDecoded().AsSpan()),
+							'-' => TextDiffer.Delete(line[1..].Replace("+", "%2b").UrlDecoded().AsSpan()),
+							_ => TextDiffer.Equal(line[1..].Replace("+", "%2b").UrlDecoded().AsSpan())
+#endif
 						};
 					}
 
@@ -258,7 +264,11 @@ public static class PatchExtension
 				var empty = true;
 				if (precontext.Length != 0)
 				{
+#if NETSTANDARD2_1_OR_GREATER
 					thediffs.Add(TextDiffer.Equal(precontext));
+#else
+					thediffs.Add(TextDiffer.Equal(precontext.AsSpan()));
+#endif
 				}
 
 				while (diffs.Any() && l1 < patchSize - patchMargin)
@@ -270,7 +280,11 @@ public static class PatchExtension
 					{
 						l2 += diffText.Length;
 						start2 += diffText.Length;
+#if NETSTANDARD2_1_OR_GREATER
 						thediffs.Add(TextDiffer.Insert(diffText));
+#else
+						thediffs.Add(TextDiffer.Insert(diffText.AsSpan()));
+#endif
 						diffs = diffs.RemoveAt(0);
 						empty = false;
 					}
@@ -278,7 +292,11 @@ public static class PatchExtension
 					{
 						l1 += diffText.Length;
 						start1 += diffText.Length;
+#if NETSTANDARD2_1_OR_GREATER
 						thediffs.Add(TextDiffer.Delete(diffText));
+#else
+						thediffs.Add(TextDiffer.Delete(diffText.AsSpan()));
+#endif
 						diffs = diffs.RemoveAt(0);
 						empty = false;
 					}
@@ -327,7 +345,11 @@ public static class PatchExtension
 					}
 					else
 					{
+#if NETSTANDARD2_1_OR_GREATER
 						thediffs.Add(TextDiffer.Equal(postcontext));
+#else
+						thediffs.Add(TextDiffer.Equal(postcontext.AsSpan()));
+#endif
 					}
 				}
 

+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/PatchOption.cs → Masuit.Tools.Abstractions/TextDiff/PatchOption.cs


+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/SemanticsImmutableList.cs → Masuit.Tools.Abstractions/TextDiff/SemanticsImmutableList.cs


+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/StringCompressor.cs → Masuit.Tools.Abstractions/TextDiff/StringCompressor.cs


+ 0 - 0
Masuit.Tools.AspNetCore/TextDiff/TextDiffConstants.cs → Masuit.Tools.Abstractions/TextDiff/TextDiffConstants.cs


+ 5 - 2
Masuit.Tools.AspNetCore/TextDiff/TextDiffer.cs → Masuit.Tools.Abstractions/TextDiff/TextDiffer.cs

@@ -31,8 +31,11 @@ public readonly record struct TextDiffer(DiffOperation Operation, string Text)
 		return Compute(text1, text2, checklines, timeoutInSeconds > 0, cts.Token);
 	}
 
-	public static ImmutableList<TextDiffer> Compute(string text1, string text2, bool checkLines, bool optimizeForSpeed, CancellationToken token)
-		=> DiffAlgorithm.Compute(text1, text2, checkLines, optimizeForSpeed, token).ToImmutableList();
+#if NETSTANDARD2_1_OR_GREATER
+	public static ImmutableList<TextDiffer> Compute(string text1, string text2, bool checkLines, bool optimizeForSpeed, CancellationToken token) => DiffAlgorithm.Compute(text1, text2, checkLines, optimizeForSpeed, token).ToImmutableList();
+#else
+	public static ImmutableList<TextDiffer> Compute(string text1, string text2, bool checkLines, bool optimizeForSpeed, CancellationToken token) => DiffAlgorithm.Compute(text1.AsSpan(), text2.AsSpan(), checkLines, optimizeForSpeed, token).ToImmutableList();
+#endif
 
 	public bool IsLargeDelete(int size) => Operation == DiffOperation.Delete && Text.Length > size;
 

+ 4 - 0
Masuit.Tools.AspNetCore/TextDiff/TextUtil.cs → Masuit.Tools.Abstractions/TextDiff/TextUtil.cs

@@ -227,7 +227,11 @@ internal static class TextUtil
 			return -1;
 		}
 
+#if NETSTANDARD2_1_OR_GREATER
 		if (loc + pattern.Length <= text.Length && text.AsSpan(loc, pattern.Length).SequenceEqual(pattern))
+#else
+		if (loc + pattern.Length <= text.Length && text.AsSpan(loc, pattern.Length).SequenceEqual(pattern.AsSpan()))
+#endif
 		{
 			return loc;
 		}

+ 0 - 1
README.md

@@ -1529,7 +1529,6 @@ public class MyService{...}
 ```
 
 ### 52. 文本对比(支持html和纯文本)
-包:Masuit.Tools.AspNetCore  
 集成案例:https://masuit.org/1889/history
 ```csharp
 var text1 = "<h1>你好 UEditorPlus</h1><p>UEditorPlus 是基于 UEditor 二次开发的富文本编辑器,让 UEditor <span style=\"color: #E36C09;\">焕<span style=\"color: #0070C0;\">然</span><span style=\"color: #31859B;\"><span style=\"color: #00B050;\">一</span><span style=\"color: #FF0000;\">新</span></span></span></p><table data-sort=\"sortDisabled\"><tbody><tr class=\"firstRow\"><td valign=\"top\" style=\"word-break: break-all;\" rowspan=\"1\" colspan=\"3\">我是表格</td></tr><tr><td width=\"273\" valign=\"top\" style=\"word-break: break-all;\">如果</td><td width=\"273\" valign=\"top\" style=\"word-break: break-all;\">有一天</td><td width=\"273\" valign=\"top\" style=\"word-break: break-all;\">我离开了</td></tr><tr><td valign=\"top\" colspan=\"1\" rowspan=\"1\" style=\"word-break: break-all;\">怎么才能</td><td valign=\"top\" colspan=\"1\" rowspan=\"1\" style=\"word-break: break-all;\">证明我</td><td valign=\"top\" colspan=\"1\" rowspan=\"1\" style=\"word-break: break-all;\">曾经来过</td></tr></tbody></table><h2>公式支持</h2><p><img src=\"https://r.latexeasy.com/image.svg?%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\" data-formula-image=\"%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\"/></p><p><br/></p>";