CI / Go Backend (push) Canceled after 0s
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
293 lines
10 KiB
C#
293 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace PCL.Core.Utils.Exts;
|
|
|
|
public static class StringConvertExtension
|
|
{
|
|
public static object? Convert(string? value, Type targetType)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(targetType);
|
|
|
|
if (targetType == typeof(string)) return value;
|
|
|
|
if (value is null)
|
|
{
|
|
if (!targetType.IsValueType || Nullable.GetUnderlyingType(targetType) is not null) return null;
|
|
return Activator.CreateInstance(targetType);
|
|
}
|
|
|
|
var converter = TypeDescriptor.GetConverter(targetType);
|
|
|
|
if (converter.CanConvertFrom(typeof(string)))
|
|
{
|
|
var c = converter.ConvertFromInvariantString(value);
|
|
return c;
|
|
}
|
|
|
|
if (typeof(IConvertible).IsAssignableFrom(targetType))
|
|
{
|
|
// ReSharper disable once RedundantSuppressNullableWarningExpression
|
|
var changed = System.Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture)!;
|
|
return changed;
|
|
}
|
|
|
|
if (targetType.IsEnum) return Enum.Parse(targetType, value, ignoreCase: true);
|
|
|
|
var parse = targetType.GetMethod("Parse",
|
|
BindingFlags.Public | BindingFlags.Static,
|
|
binder: null, types: [typeof(string)], modifiers: null);
|
|
if (parse is not null) return parse.Invoke(null, [value]);
|
|
|
|
throw new NotSupportedException($"无法将字符串转换为类型 {targetType.FullName}");
|
|
}
|
|
|
|
public static T? Convert<T>(this string? value)
|
|
{
|
|
var obj = Convert(value, typeof(T));
|
|
if (obj is null) return default;
|
|
return (T)obj;
|
|
}
|
|
}
|
|
|
|
public static class StringExtension
|
|
{
|
|
public static string? ConvertToString(object? obj)
|
|
{
|
|
if (obj is null) return null;
|
|
if (obj is string s) return s;
|
|
|
|
var converter = TypeDescriptor.GetConverter(obj.GetType());
|
|
if (converter.CanConvertTo(typeof(string)))
|
|
{
|
|
object? o = converter.ConvertToInvariantString(obj);
|
|
return o as string;
|
|
}
|
|
|
|
if (obj is IFormattable fmt) return fmt.ToString(null, CultureInfo.InvariantCulture);
|
|
|
|
return obj.ToString();
|
|
}
|
|
|
|
public static string? ConvertToString<T>(this T? value) => ConvertToString((object?)value);
|
|
|
|
private static readonly char[] _B36Map = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
|
|
|
|
extension(string input)
|
|
{
|
|
public string FromB10ToB36()
|
|
{
|
|
var n = BigInteger.Parse(input);
|
|
var s = new List<char>();
|
|
while (n > 0)
|
|
{
|
|
var i = (n % 36).ToByteArray()[0];
|
|
s.Add(_B36Map[i]);
|
|
n /= 36;
|
|
}
|
|
s.Reverse();
|
|
return string.Join("", s);
|
|
}
|
|
|
|
public string FromB36ToB10()
|
|
{
|
|
var ns = input.Select(c => (c is >= '0' and <= '9') ? c - '0' : c - 'A' + 10).ToArray();
|
|
var nb = ns.Aggregate(new BigInteger(0), (n, i) => n * 36 + i);
|
|
return nb.ToString();
|
|
}
|
|
}
|
|
|
|
private static readonly char[] _B32Map = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray();
|
|
|
|
extension(string input)
|
|
{
|
|
/// <summary>
|
|
/// 将 Base10 文本重新编码为 Base32 文本。
|
|
/// </summary>
|
|
public string FromB10ToB32()
|
|
{
|
|
var n = BigInteger.Parse(input);
|
|
var s = new List<char>();
|
|
while (n > 0)
|
|
{
|
|
var i = (n % 32).ToByteArray()[0];
|
|
s.Add(_B32Map[i]);
|
|
n /= 32;
|
|
}
|
|
s.Reverse();
|
|
return string.Join("", s);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 Base32 文本重新编码为 Base10 文本。
|
|
/// </summary>
|
|
public string FromB32ToB10()
|
|
{
|
|
var ns = input.Select(Parse).ToArray();
|
|
var nb = ns.Aggregate(new BigInteger(0), (n, i) => n * 32 + i);
|
|
return nb.ToString();
|
|
|
|
int Parse(char c) => c switch
|
|
{
|
|
>= '2' and <= '9' => c - '2',
|
|
>= 'A' and <= 'H' => c - 'A' + 8,
|
|
>= 'J' and <= 'N' => c - 'J' + 16,
|
|
>= 'P' and <= 'Z' => c - 'P' + 21,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(input), $"Character '{c}' out of Base32 range")
|
|
};
|
|
}
|
|
|
|
public string FromB64ToB64UrlSafe() => input.Replace("+", "-").Replace("/", "_");
|
|
public string FromB64UrlSafeToB64() => input.Replace("-", "+").Replace("_", "/");
|
|
|
|
public byte[] FromB64ToBytes()
|
|
{
|
|
switch (input.Length % 4)
|
|
{
|
|
case 3:
|
|
input += "===";
|
|
break;
|
|
case 2:
|
|
input += "==";
|
|
break;
|
|
case 1:
|
|
input += "=";
|
|
break;
|
|
}
|
|
|
|
return Convert.FromBase64String(input);
|
|
}
|
|
|
|
public byte[] FromB64UrlSafeToBytes() => input.FromB64UrlSafeToB64().FromB64ToBytes();
|
|
}
|
|
|
|
extension(string input)
|
|
{
|
|
|
|
public T ParseToEnum<T>() where T : struct, Enum
|
|
{
|
|
if (String.IsNullOrWhiteSpace(input))
|
|
{
|
|
return (T)(object)0;
|
|
}
|
|
else if (int.TryParse(input, out int numericValue))
|
|
{
|
|
return (T)(object)numericValue;
|
|
}
|
|
else
|
|
{
|
|
return Enum.Parse<T>(input, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
extension([NotNullWhen(false)] string? value)
|
|
{
|
|
/// <summary>
|
|
/// <see cref="string.IsNullOrEmpty"/> 的扩展方法。
|
|
/// </summary>
|
|
public bool IsNullOrEmpty() => string.IsNullOrEmpty(value);
|
|
|
|
/// <summary>
|
|
/// <see cref="string.IsNullOrWhiteSpace"/> 的扩展方法。
|
|
/// </summary>
|
|
public bool IsNullOrWhiteSpace() => string.IsNullOrWhiteSpace(value);
|
|
}
|
|
|
|
/// <param name="input">文本</param>
|
|
extension(string? input)
|
|
{
|
|
/// <summary>
|
|
/// 当文本为空时返回替代文本,否则返回原来的文本。
|
|
/// </summary>
|
|
/// <param name="replacement">替代文本</param>
|
|
public string ReplaceNullOrEmpty(string? replacement = null)
|
|
=> string.IsNullOrEmpty(input) ? (replacement ?? string.Empty) : input;
|
|
|
|
/// <summary>
|
|
/// 替换指定文本中的所有换行符。
|
|
/// </summary>
|
|
/// <param name="replacement">用于替换的文本</param>
|
|
/// <returns>替换后的文本</returns>
|
|
public string ReplaceLineBreak(string replacement = " ")
|
|
=> input?.Replace(RegexPatterns.NewLine, replacement) ?? string.Empty;
|
|
|
|
/// <summary>
|
|
/// 替换指定文本中所有匹配正则表达式的部分。
|
|
/// </summary>
|
|
/// <param name="regex">正则表达式</param>
|
|
/// <param name="replacement">用于替换的文本</param>
|
|
/// <returns>替换后的文本</returns>
|
|
[return: NotNullIfNotNull(nameof(input))]
|
|
public string? Replace(Regex regex, string replacement)
|
|
=> input is null ? null : regex.Replace(input, replacement);
|
|
|
|
/// <summary>
|
|
/// 判断指定文本是否能成功匹配正则表达式。
|
|
/// </summary>
|
|
/// <param name="regex">正则表达式</param>
|
|
/// <returns>若匹配成功则为 <c>true</c>,若文本为 <c>null</c> 或匹配不成功则为 <c>false</c></returns>
|
|
public bool IsMatch(Regex regex)
|
|
=> input is not null && regex.IsMatch(input);
|
|
}
|
|
|
|
extension(string str)
|
|
{
|
|
/// <summary>
|
|
/// 查找并返回指定文本中所有与正则表达式匹配的部分。
|
|
/// </summary>
|
|
public List<string> RegexSearch(Regex regex)
|
|
{
|
|
var result = new List<string>();
|
|
var regexSearchRes = regex.Matches(str);
|
|
if (regexSearchRes.Count == 0) return result;
|
|
result.AddRange(from Match item in regexSearchRes select item.Value);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断指定文本是否在 ASCII 范围内。
|
|
/// </summary>
|
|
// ReSharper disable once InconsistentNaming
|
|
public bool IsASCII()
|
|
{
|
|
return str.All(c => c < 128);
|
|
}
|
|
|
|
public bool StartsWithF(string prefix, bool ignoreCase = false)
|
|
=> str.StartsWith(prefix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public bool EndsWithF(string suffix, bool ignoreCase = false)
|
|
=> str.EndsWith(suffix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public bool ContainsF(string subStr, bool ignoreCase = false)
|
|
=> str.Contains(subStr, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public int IndexOfF(string subStr, bool ignoreCase = false)
|
|
=> str.IndexOf(subStr, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public int IndexOfF(string subStr, int startIndex, bool ignoreCase = false)
|
|
=> str.IndexOf(subStr, startIndex, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public int LastIndexOfF(string subStr, bool ignoreCase = false)
|
|
=> str.LastIndexOf(subStr, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
|
|
public int LastIndexOfF(string subStr, int startIndex, bool ignoreCase = false)
|
|
=> str.LastIndexOf(subStr, startIndex, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
|
}
|
|
|
|
extension(string hex)
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public byte[] HexToBytes() => Convert.FromHexString(hex);
|
|
}
|
|
}
|