Files
MRCC/launcher/PCL-CE/PCL.Core/Utils/Exts/StringExtension.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

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);
}
}