Yoko.Tool.Core 3.1.7

dotnet add package Yoko.Tool.Core --version 3.1.7                
NuGet\Install-Package Yoko.Tool.Core -Version 3.1.7                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Yoko.Tool.Core" Version="3.1.7" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Yoko.Tool.Core --version 3.1.7                
#r "nuget: Yoko.Tool.Core, 3.1.7"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Yoko.Tool.Core as a Cake Addin
#addin nuget:?package=Yoko.Tool.Core&version=3.1.7

// Install Yoko.Tool.Core as a Cake Tool
#tool nuget:?package=Yoko.Tool.Core&version=3.1.7                

[TOC]

Yoko.Tool.Core

懒人必备工具箱

NuGet Badge

📚 功能示例说明

📒 值类型转换

// 值类型转换
.ToInt32()
.ToBool()
.ToLong()
.ToDouble()
.ToDecimal()
.ToFloat()
    
// 时间操作
DateTimeExtensions.GetDaysWithWeekdays(2024, 9);     //获取指定月份的所有日期和星期。第三个参数可以获取完整的星期名
.ToDateTime()  // 字符串 转 时间 
.ToStartTime() // DateTime转换成开始时间00:00:00
.ToEndTime()   // DateTime转换成结束时间23:59:59
.ToUnixTime()  // DateTime时间转Unix时间戳(默认精确到毫秒13位)
.ToUnixTimeS() // DateTime时间转Unix时间戳(默认精确到秒10位)
.ToLocalTime() // Unix时间戳转DateTime本地时间(yyyy/MM/dd hh:MM:ss)
.DateDiff()    // 返回时间差(N小时前,N分钟前···)
.GetDiffTime() // 计算2个时间差(相差多少年月日时分秒)
.GetDaysOfMonth()      // 本月有多少天
.GetWeekNameOfDay()    // 返回当前日期的星期名称
DateTime.Now.GetDateTime();    //返回标准时间格式(yyyy-MM-dd HH:mm:ss)
DateTime.Now.GetDateTimeF();    //返回标准时间格式(yyyy-MM-dd HH:mm:ss:fffffff)
DateTime.Now.GetTotalSeconds(); // 获取该时间相对于1970-01-01 00:00:00的秒数
DateTime.Now.GetTotalMilliseconds(); // 获取该时间相对于1970-01-01 00:00:00的毫秒数
DateTime.Now.GetTotalMicroseconds(); // 获取该时间相对于1970-01-01 00:00:00的微秒数

//字符串操作
.SplitCsv()      // 分割逗号的字符串为List[]、string[]
.SplitCsvToInt() // 分割逗号的字符串为int[]   
.SplitCsvToLong()  //将包含指定分隔符的字符串转换为 long 类型的数组
.ToIntList()     // string数组转int数组
.ToFilterStr()   // 过滤特殊字符[]^-_*×――(^)|'$%~!@#$…&%¥—+=<>《》!!???::•`·、。,;,.;\"‘’“”-
.ToByteArray()   // string转换成字节数组
.ToJson()        // 将一个对象转为Json格式字符串
.ToModel()       // 反序列化一个JSON格式字符串为对象
.ToDynamic()     // 将Json转为一个动态类型(运行时解析)
.ToMask()        // 字符串敏感信息掩码  (123***789)
.ToMaskEmail()   // 邮箱敏感信息掩码  (123****@163.com)
.ByteToHexString()  // 字节数组转16进制字符

//值计算
//除法计算 a/b (double)
//type=1 浮点数,保留2位小数(四舍五入):12.34
//type=2 百分比:12.23%
//type=3 带有逗号分隔符,保留小数位数(四舍五入):2,500,000.00
//type=4 货币表示,保留2位小数(四舍五入):¥12.34
A.DividedBy(B,type);


//DataTable 操作
DataTableHelper<T>.ToList(dt);
DataTableHelper<T>.ToModel(dt);
📖 Byte扩展
  • ToBase64():将 byte[] 转换为 Base64 字符串。

  • ToHexString():将 byte[] 转换为十六进制字符串,常用于显示加密或哈希结果。

  • SequenceEqualTo():比较两个 byte[] 是否相等,替代直接 SequenceEqual() 的调用。

  • ToUtf8String():将 byte[] 转换为 UTF-8 字符串,适用于文本数据。

  • FromBase64String():从 Base64 字符串恢复为 byte[]

  • FromHexString():从十六进制字符串恢复为 byte[],适合用于解析数据。

byte[] data = Encoding.UTF8.GetBytes("Example Data");
Console.WriteLine("Base64: " + data.ToBase64());
Console.WriteLine("Hex: " + data.ToHexString());

byte[] newData = "Example Data".ToBase64().FromBase64String();
Console.WriteLine("是否相等: " + data.SequenceEqualTo(newData));

byte[] hexData = "4d6963726f736f6674".FromHexString();
Console.WriteLine("Hex to String: " + hexData.ToUtf8String());

📒 值判断

// 在范围?  
/**
int num = 50;
if (num.IsInRange(100, 1000)) { }
string value = "a";
if (value.IsIn("a", "b", "c")) { }
*/
IsInRange() 
IsInRange() 
IsIn()
IsContainsIn()

IsNullOrEmpty()   // 是null或"" ? 
IsValuable()      // 有值?(与IsNullOrEmpty相反)
IsZero()          // 是零?
IsInt()           // 是Int?
IsNoInt()         // 不是Int?
IsMoney()         // 是金钱? 
IsDate()          // 是时间?
IsDate()          // 是邮箱?
IsMobile()        // 是手机号?(弱验证)
IsPhoneNumber()   // 是手机号?(强验证)
IsTelephone()     // 是座机?  
IsIDcard()        // 是身份证?
IsFax()           // 是传真? 
IsMatch()         // 是适合正则匹配? 
IsTrue()          // 是true?
IsFalse()         // 是false?

📒 随机数


// 生成一个真正的随机数
int randomNum = RandomExt.StrictNext(0, 100);
Console.WriteLine($"真正的随机数: {randomNum}");

// 生成一个正态分布的随机数
double gaussNum = RandomExt.NextGauss(0, 1);
Console.WriteLine($"正态分布的随机数: {gaussNum}");

// 根据规则生成随机字符串
// "?"代表一个字符,"#"代表一个一位数字,"*"代表一个字符串或一个一位数字
string patternString = RandomExt.GetRandStringByPattern("???###***");
Console.WriteLine($"根据规则生成的随机字符串: {patternString}");

// 生成一个随机数值
int formatedNumeric = RandomExt.GetFormatedNumeric(0, 100);
Console.WriteLine($"随机数值: {formatedNumeric}");

// 生成一个指定长度和字符的随机字符串
string randomString = RandomExt.GetRandomString("abcdefghijklmnoaspqrstuvwxyzABCDEFGHIJKLMNasdfOPQRSTUVWXYZ0123456789", 10);
Console.WriteLine($"指定长度和字符的随机字符串: {randomString}");

// 生成一个指定长度的随机字符串
string randomString2 = RandomExt.GetRandomString(10);
Console.WriteLine($"指定长度的随机字符串: {randomString2}");

// 生成一个指定长度的纯字母随机字符串
string randWord = RandomExt.GetRandWord(7);
Console.WriteLine($"指定长度的纯字母随机字符串: {randWord}");

// 生成一个指定长度的纯数字随机数字串
string randomNumString = RandomExt.GetRandomNum(5);
Console.WriteLine($"指定长度的纯数字随机数字串: {randomNumString}");

// 生成一个按照年月时分秒随机数生成的文件名
string randomFileName = RandomExt.GetFileRndName();
Console.WriteLine($"按照年月时分秒随机数生成的文件名: {randomFileName}");

📒 集合/字典操作

📖 对集合进行分组
/// 对集合进行分组
/// 指定要拆分成几个集合,集合中子集个数动态分配。
/// 如果原集合中子集的个数小于等于要分组数,既source.Count 小于等于 size,建议使用source.Chunk(1).ToList()
// 创建一个列表
List<int> numbers = Enumerable.Range(1, 10).ToList();

// 使用ChunkList方法将列表分成3个组
List<int[]> chunks = numbers.ChunkList(3);

// 打印每个组的内容
for (int i = 0; i < chunks.Count; i++)
{
    Console.WriteLine($"Group {i + 1}: {string.Join(", ", chunks[i])}");
}
📖 对参数进行排序
//==============对参数进行排序,并返回格式化的字符串。======================
// 创建一个字典
Dictionary<string, string> parameters = new Dictionary<string, string>
{
    { "param1", "value1" },
    { "param2", "value2" },
    { "param3", "value3" }
};

// 使用OrderByASCII方法对字典进行排序并返回格式化的字符串
string orderedParameters = parameters.OrderByASCII();     //参数区分大小写
//string orderedParameters = parameters.OrderByASCII(false);   //不区分大小写

// 打印排序后的参数
Console.WriteLine($"Ordered parameters: {orderedParameters}");

📒 【弃用】字符串相似度

📕 请使用 F23.StringSimilarity

📖 Jaccard相似度

这种方法主要是通过计算两个集合交集的大小与并集的大小的比例来得到相似度

📖 Sorensen Dice相似度系数

与Jaccard相似度类似,但计算方式略有不同,它是通过计算两个集合交集的2倍除以两个集合的和来得到相似度

📖 汉明距离

明距离是编辑距离中的一个特殊情况,仅用来计算两个等长字符串中不一致的字符个数


var distance = "我爱编程".HammingDistance("我喜欢编程");
var distance = new SimHash("我爱编程").HammingDistance(new SimHash("我喜欢编程"));

// 创建两个字符串
string str1 = "我爱编程";
string str2 = "我喜欢编程";

// 创建两个SimHash对象
SimHash simHash1 = new SimHash(str1);
SimHash simHash2 = new SimHash(str2);

// 计算两个字符串的汉明距离
int distance = simHash1.HammingDistance(simHash2);

// 打印汉明距离
Console.WriteLine($"The Hamming distance between \"{str1}\" and \"{str2}\" is {distance}.");


var target = "12345678";
var targetSimHash = new SimHash(target);

var list = new List<string>();  // list 为一个 List<string> 的集合
var similarPlates = new List<string>();

foreach (var plate in list)
{
    var plateSimHash = new SimHash(plate);
    var similarity = targetSimHash.Similarity(plateSimHash);
    if (similarity >= 0.9)  // 设置阈值为0.9,即相似度为90%
    {
        similarPlates.Add(plate);
    }
}

📒 一般密码加解密/验证

// MD5 加密、加盐 
"123456".ToMD5()
"123456".ToMD5("abc123")

// 两次加密、加盐  
"123456".ToMD5Double()
"123456".ToMD5Double("abc123")
// AES加密、解密(密钥)  
"123456".ToAES()
"123456".ToAESDecrypt()
// DES加密、解密(密钥)  
"123456".ToDES()
"123456".ToDESDecrypt()
// RSA加密、解密
RsaKey rsaKey = RsaCrypt.GenerateRsaKeys(); //生成RSA密钥对
string encrypt = "123456".RSAEncrypt(rsaKey.PublicKey); //公钥加密
string s = encrypt.RSADecrypt(rsaKey.PrivateKey);//私钥解密
// 用于生成一般账号的加密密码  
// 默认MD5+哈希。生成密码组成: 标识头 + 迭代 + 哈希大小 + salt值 + 哈希值
// 设置标识头Key:YokoPassWord.Key = "yoko";
// 值表现为 : yoko:64000:18:salt值:哈希值 
"123456".ToPassWord("要MD5加密?")

// 验证密码 
 "目标比对密码".IsTrue("输入的密码")

📒 国密

请安装库 Yoko.Tool.Security NuGet Badge

  • 目前有的算法:AES,DES,RC4,RSA,SM2,SM3,SM4
📖 RC4
/// <summary>
/// RC4
/// </summary>
public void Rc4()
{
    const string data = "Microsoft";
    var key = "123456"u8.ToArray();
    var byte_data = Encoding.UTF8.GetBytes(data);
    var secret = Rc4Crypt.Encrypt(byte_data, key);
    var base64 = secret.ToBase64();
    // TZEdFUtAevoL
    var data_result = Rc4Crypt.Decrypt(secret, key);
    var result = Encoding.UTF8.GetString(data_result);
    // result == data
}
📖 RSA
// XML私钥转换为Base64格式
var pri = RsaKeyConverter.ToBase64PrivateKey(PrivateKey);
// XML公钥转换为Base64格式
var pub = RsaKeyConverter.ToBase64PublicKey(PublicKey);

 // 一样可以将Base64格式转换为XML格式
var xml_pri = RsaKeyConverter.ToXmlPrivateKey(pri);
var xml_pub = RsaKeyConverter.ToXmlPublicKey(pub);

//生成密钥对
var data = RsaCrypt.GenerateKey(520);
Console.WriteLine($"公钥:{data.PublicKey}");
Console.WriteLine($"私钥:{data.PrivateKey}");

/// <summary>
/// RSA加密解密测试
/// </summary>
public void RsaEncryptAndDecrypt()
{
    const string data = "Microsoft";
    // 将原文解析到二进制数组格式
    var byte_data = Encoding.UTF8.GetBytes(data);
    RsaCrypt.Encrypt(key.PublicKey, byte_data, out var secret_data);
    var secret_str = secret_data.ToBase64();
    Console.WriteLine(secret_str);
    RsaCrypt.Decrypt(key.PrivateKey, secret_str.FromBase64(), out var data_byte);
    var result = Encoding.UTF8.GetString(data_byte);
}
📖 SM2
// 1. 生成密钥对
Sm2Crypt.GenerateKey(out byte[] publicKey, out byte[] privateKey);
Console.WriteLine("生成的公钥: " + Convert.ToBase64String(publicKey));
Console.WriteLine("生成的私钥: " + Convert.ToBase64String(privateKey));

// 设置待加密和签名的数据
string message = "Hello, SM2!";
byte[] data = Encoding.UTF8.GetBytes(message);

// 2. 使用公钥进行加密
byte[] encryptedData = Sm2Crypt.Encrypt(publicKey, data);
Console.WriteLine("加密后的数据: " + Convert.ToBase64String(encryptedData));

// 3. 使用私钥进行解密
byte[] decryptedData = Sm2Crypt.Decrypt(privateKey, encryptedData);
Console.WriteLine("解密后的数据: " + Encoding.UTF8.GetString(decryptedData));

// 4. 使用私钥签名
byte[] signature = Sm2Crypt.Signature(privateKey, data);
Console.WriteLine("签名: " + Convert.ToBase64String(signature));

// 5. 使用公钥验签
bool isVerified = Sm2Crypt.Verify(publicKey, data, signature);
Console.WriteLine("验签结果: " + (isVerified ? "成功" : "失败"));
📖 SM3
 private const string data = "Microsoft";

/// <summary>
/// SM3测试16进制字符串格式
/// </summary>
public void SM3HexString()
{
    var byte_data = Sm3Crypt.Crypt(data);
    var hex = byte_data.ToHex();
    hex.ToUpper().Should().Be("1749CE3E4EF7622F1EBABB52078EC86309CABD5A6073C8A0711BF35E19BA51B8");
}

/// <summary>
/// SM3测试Base64字符串格式
/// </summary>
public void SM3Base64()
{
    var byte_data = Sm3Crypt.Crypt(data);
    var base64 = byte_data.ToBase64();
    base64.ToUpper().Should().Be("F0NOPK73YI8EURTSB47IYWNKVVPGC8IGCRVZXHM6UBG=");
}
📖 SM4
/// <summary>
/// SM4ECB模式加密到Base64格式
/// </summary>
public void Sm4EncryptECBToBase64()
{
    const string data = "Microsoft";
    // 将原文解析到二进制数组格式
    var byte_data = Encoding.UTF8.GetBytes(data);
    // 进制格式密钥加密数据
    var result = Sm4Crypt.EncryptECB("701d1cc0cfbe7ee11824df718855c0c6", true, byte_data);
    // 获取Base64格式的字符串结果
    var base64 = result.ToBase64();
    // ThRruxZZm1GrHE5KkP4UmQ==
}

/// <summary>
/// SM4ECB模式解密Base64格式到字符串
/// </summary>
public void Sm4DecryptECBTest()
{
    // Base64格式的
    const string data = "ThRruxZZm1GrHE5KkP4UmQ==";
    // 将Base64格式字符串转为 byte[]
    var byte_data = Convert.FromBase64String(data);
    // 通过16进制格式密钥解密数据
    var result = Sm4Crypt.DecryptECB("701d1cc0cfbe7ee11824df718855c0c6", true, byte_data);
    // 解析结果获取字符串
    var str = Encoding.UTF8.GetString(result);
    // Microsoft
}

/// <summary>
/// SM4ECB模式加密到16进制字符串
/// </summary>
public void Sm4EncryptECBToHex16()
{
    const string data = "Microsoft";
    // 将原文解析到二进制数组格式
    var byte_data = Encoding.UTF8.GetBytes(data);
    // 使用16位长度的密钥加密
    var result = Sm4Crypt.EncryptECB("1cc0cfbe7ee11824", false, byte_data);
    // 将结果转为16进制字符串
    var hex = result.ToHexString();
    // D265DF0510C05FE836D3113B3ACEC714
}

/// <summary>
/// SM4ECB模式解密16进制字符串格式密文
/// </summary>
public void Sm4DecryptECBTest2()
{
    const string data = "D265DF0510C05FE836D3113B3ACEC714";
    var byte_data = data.FromHex();
    var result = Sm4Crypt.DecryptECB("1cc0cfbe7ee11824", false, byte_data);
    // 解析结果获取字符串
    var str = Encoding.UTF8.GetString(result);
    // Microsoft
}

/// <summary>
/// SM4CBC模式加密到Base64格式
/// </summary>
public void Sm4EncryptCBCTest()
{
    const string data = "Microsoft";
    var byte_data = Encoding.UTF8.GetBytes(data);
    var result = Sm4Crypt.EncryptCBC("701d1cc0cfbe7ee11824df718855c0c6", true, "701d1cc0cfbe7ee11824df718855c0c5", byte_data);
    var base64 = result.ToBase64();
    // Q2iUaMuSHjLvq6GhUQnGTg==
}

/// <summary>
/// SM4CBC模式从Base64解密
/// </summary>
public void Sm4DecryptCBCTest()
{
    const string data = "Q2iUaMuSHjLvq6GhUQnGTg==";
    var byte_data = Convert.FromBase64String(data);
    var result = Sm4Crypt.DecryptCBC("701d1cc0cfbe7ee11824df718855c0c6", true, "701d1cc0cfbe7ee11824df718855c0c5", byte_data);
    var str = Encoding.UTF8.GetString(result);
    // Microsoft
}

/// <summary>
/// SM4CBC模式加密到16进制字符串
/// </summary>
public void Sm4EncryptCBCTest2()
{
    const string data = "Microsoft";
    var byte_data = Encoding.UTF8.GetBytes(data);
    var result = Sm4Crypt.EncryptCBC("1cc0cfbe7ee11824", false, "1cc0cfbe7ee12824", byte_data);
    var hex = result.ToHexString();
    // 1BD7A32E49B60B17698AAC9D1E4FEE4A
}

/// <summary>
/// SM4CBC模式从Hex16解密到字符串
/// </summary>
public void Sm4DecryptCBCTest2()
{
    const string data = "1BD7A32E49B60B17698AAC9D1E4FEE4A";
    var byte_data = data.FromHex();
    var result = Sm4Crypt.DecryptCBC("1cc0cfbe7ee11824", false, "1cc0cfbe7ee12824", byte_data);
    var str = Encoding.UTF8.GetString(result);
    // Microsoft
}

📒 经纬度操作

//根据经纬度计算两点间距离
YokoLngLat.GetDistance(x1,y1,x2,y2)
    
//求一个包含经纬度集合的距离和
YokoLngLat.GetDistanceSum(dict);

//坐标系互转
YokoLngLat.GPSToAMap()   // GPS转高德(WGS84 --> GCJ-02)
YokoLngLat.AMapToGPS()   // 高德转GPS(GCJ-02 --> WGS84)
YokoLngLat.AMapToBMap()  // 高德转百度(GCJ-02 --> BD-09)
YokoLngLat.BMapToAMap()  // 百度转高德(BD-09 --> GCJ-02)

📒 URL 操作

📖 分解URL信息
string url = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9";

// 分解URL信息  UrlSplit() 
var data = url.UrlSplit();

// 协议
data.Scheme;
// 服务器地址
data.Host;
// 端口号
data.Port;
// 绝对路径
data.AbsolutePath;
// 本地路径
data.LocalPath;
// 路径的结构数组
data.Segments;
// 查询信息部分
data.Query;
// 问号(?) 分割后的部分 
data.PathAndQuery;

json解读:

{
    "Scheme": "https",
    "Host": "www.baidu.com",
    "Port": 443,
    "AbsolutePath": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
    "LocalPath": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
    "Segments": [
        "/",
        "img/",
        "PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
    ],
    "Query": "?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9",
    "PathAndQuery": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9"
}
📖 替换主机地址
string url = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9";

// 替换主机地址
var data = url.ReplaceHost("192.168.1.100");

📖 获取一个网络文件(图片)的文件名
string url = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a95bb502a395696f3%2F2022-09-05T01";

//获取一个网络图片的文件名
var data = url.GetFileName();
// 输出 PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png

// 并更改文件扩展名
var data2 = url.GetFileName(".jpg");
// 输出 PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.jpg

📒 IO 操作

📖 ZIP 解压缩
// 压缩文件夹
string sourceDirectory = @"C:\Example\SourceFolder";
string destinationZipFile = @"C:\Example\CompressedFolder.zip";
try
{
    ZipHelper.ZipDirectory(sourceDirectory, destinationZipFile);
    Console.WriteLine("文件夹已成功压缩到 " + destinationZipFile);
}
catch (Exception ex)
{
    Console.WriteLine("压缩文件夹时出错: " + ex.Message);
}

// 解压Zip文件
string unzipDestinationDirectory = @"C:\Example\UnzippedFolder";
try
{
    ZipHelper.UnZipFile(destinationZipFile, unzipDestinationDirectory);
    Console.WriteLine("文件已成功解压到 " + unzipDestinationDirectory);
}
catch (Exception ex)
{
    Console.WriteLine("解压文件时出错: " + ex.Message);
}

// 压缩单个文件
string sourceFile = @"C:\Example\SourceFile.txt";
string destinationFileZip = @"C:\Example\CompressedFile.zip";
try
{
    ZipHelper.ZipFromFile(sourceFile, destinationFileZip);
    Console.WriteLine("文件已成功压缩到 " + destinationFileZip);
}
catch (Exception ex)
{
    Console.WriteLine("压缩文件时出错: " + ex.Message);
}
📖字节数组 解压缩
// 原始数据
string originalText = "This is a sample text to demonstrate compression and decompression.";
byte[] originalData = Encoding.UTF8.GetBytes(originalText);
Console.WriteLine("原始数据长度: " + originalData.Length);

// 压缩数据
byte[] compressedData = CompressionHelper.Compress(originalData);
Console.WriteLine("压缩后的数据长度: " + compressedData.Length);

// 解压数据
byte[] decompressedData = CompressionHelper.Decompress(compressedData);
string decompressedText = Encoding.UTF8.GetString(decompressedData);
Console.WriteLine("解压后的数据长度: " + decompressedData.Length);
Console.WriteLine("解压后的文本: " + decompressedText);

// 检查解压后的数据是否与原始数据相同
Console.WriteLine("解压是否与原始数据一致: " + (originalText == decompressedText ? "是" : "否"));

📖简单的文件操作辅助
//根据图片地址获取获取Stream流
"https://example.com/sample.jpg".GetStreamAsync();

// 读取 Stream 到字节数组
.ReadByte()

// 将网络图片转换为 Base64
string imageUrl = "https://example.com/sample.jpg";
string base64String = await imageUrl.ImageToBase64Async();

// 递归获取指定路径下的所有文件
string folderPath = @"C:\Example\Folder";
var filePaths = folderPath.GetFilePath(".txt");
Console.WriteLine("文件夹中的 .txt 文件:");
filePaths.ForEach(Console.WriteLine);

// 将本地文件转换为 Base64
string filePath = @"C:\Example\file.txt";
try
{
    string fileBase64 = YokoFile.FileToBase64(filePath);
    Console.WriteLine("本地文件的 Base64 编码: " + fileBase64);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine(ex.Message);
}

// 获取文件大小
long fileSize = YokoFile.GetFileSize(filePath);
Console.WriteLine("文件大小 (字节): " + (fileSize >= 0 ? fileSize.ToString() : "文件不存在"));
📖 图片操作 jpeg、png 和 webp

包: Yoko.Images.Webp

从静态路径或者网络路径转换、调整大小和压缩 jpeg、png 和 webp 图像

或者使用 SkiaSharp 库更好

📒 时间段操作

📖 判断是否在某个时间段内、是否包含某个时间段、两个时间段是否相交、连接两个时间段
var range = new DateTimeRange("2021-9-3", "2021-9-5".ToDateTime());

//连接两个时间段,结果:2021-9-3~2021-9-6
range.Union("2021-9-4".ToDateTime(), "2021-9-6".ToDateTime()); 

//判断是否在某个时间段内,true
range.In("2021-9-3".ToDateTime(), "2021-9-6".ToDateTime());

//两个时间段是否相交,(true,2021-9-3~2021-9-4)
var (intersected,range2) = range.Intersect("2021-9-4".ToDateTime(), "2021-9-6".ToDateTime());

//判断是否包含某个时间段,true
range.Contains("2021-9-3".ToDateTime(), "2021-9-4".ToDateTime());

📖 获取所有的补集时间段( 默认过滤差值为1min )
  var range = new DateTimeRange(new DateTime(2021, 9, 3, 7, 8, 10)
                , new DateTime(2021, 9, 4, 18, 0, 0));
  Console.WriteLine($"时段:{range}");
  Console.WriteLine();
  Console.WriteLine();
  var ranges = new List<DateTimeRange>()
  {
      new DateTimeRange(new DateTime(2021, 9, 3, 10, 0, 0), new DateTime(2021, 9, 3, 15, 0, 0)),
      new DateTimeRange(new DateTime(2021, 9, 3, 20, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
      new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 8, 0, 0)),
      new DateTimeRange(new DateTime(2021, 9, 4, 12, 0, 0), new DateTime(2021, 9, 4, 18, 30, 59)),
      new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
  };
  var chargingPeriods = range.GetComplementPeriods(ranges, TimeSpan.FromMinutes(1));

  foreach (var chargingPeriod in chargingPeriods)
  {
      Console.WriteLine($"补集时段:{chargingPeriod}");
  }
📖 判断时间段是否与一组时间段相交/包含,并返回所有相交/包含的时间段
  var range = new DateTimeRange(new DateTime(2021, 9, 3, 7, 8, 10)
                , new DateTime(2021, 9, 4, 18, 0, 0));
  Console.WriteLine($"时段:{range}");
  Console.WriteLine();
  Console.WriteLine();
  var ranges = new List<DateTimeRange>()
  {
      new DateTimeRange(new DateTime(2021, 9, 3, 10, 0, 0), new DateTime(2021, 9, 3, 15, 0, 0)),
      new DateTimeRange(new DateTime(2021, 9, 3, 20, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
      new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 8, 0, 0)),
      new DateTimeRange(new DateTime(2021, 9, 4, 12, 0, 0), new DateTime(2021, 9, 4, 18, 30, 59)),
      new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
  };

  var freePeriods = range.IntersectWith(ranges);

  foreach (var free in freePeriods)
  {
      Console.WriteLine($"相交/包含 时段:{free}");
  }

📒 邮件发送

// 异步发送邮件 SendEmailAsync  
// 同步发送 SendEmail
new Email()
{
    SmtpServer = "smtp.qq.com",// SMTP服务器
    SmtpPort = 587, // SMTP服务器端口
    EnableSsl = true,//使用SSL
    Username = "88888888888@qq.com",// 邮箱用户名
    Password = "123456789",// 邮箱密码
    Tos = "111111111@qq.com,22222222@qq.com", //多个收件人用,隔开
    Subject = "测试邮件",//邮件标题
    Body = "Hello,WoCao",//邮件内容
}.SendEmailAsync(s =>
{
    // 发送成功后的回调
    // 异步发送邮件,如果回调方法中参数不为"true"则表示发送失败
    Console.WriteLine(s);
});

📒 雪花ID优化版

  • 雪花算法实现方法有很多,它生成的ID更短、速度更快

  • 可在单机或分布式环境生成数字型唯一ID,新增预留位,支持服务器时间回拨

  • 兼容传统的经典雪花算法

📖 调用示例

1、全局初始化,只需全局一次(必须在第2步之前设置)

//初始化  1.0.4+
YokoId.Init(1);

//================老版本================
//初始化
YokoId.SetIdGenerator(new IdGeneratorOptions(1));

//WorkerId  [1,63]

2、生成ID

var newId = YokoId.NextId();

📖 参数设置

  • WorkerId,机器码,最重要参数,无默认值,必须 全局唯一,必须 程序设定,最大值63 特别提示:如果一台服务器部署多个独立服务,需要为每个服务指定不同的 WorkerId。
  • WorkerIdBitLength,机器码位长,决定 WorkerId 的最大值,默认值6,取值范围 [1, 19]

📒 【已弃用】自动更新 IIS 上的 Asp.Net Core应用程序 2.1.1 ~ 2.2.0

  • 通过上传 zip 文件,更新部署到正在IIS上运行的 Asp.Net Core 应用程序

注意事项:

1、将要发布更新的应用程序,全部 压缩打包成 publish.zip 文件

2、zip 部署待更新文件应与站点位于同一位置。例如,web.config 应该直接在 zip 文件内,而不是在 zip 文件内的文件夹内

3、上传尽量使用临时文件名,直到上传完成,上传完成后再重命名为 publish.zip

4、上传完publish.zip后,会自动解压更新程序,并产生对应的临时文件。此时刷新应用程序,测试程序调用是否正常,临时文件会陆续自动删除

打开Startup.cs

using Yoko.Tool.ZipDeploy;

public void ConfigureServices(IServiceCollection services)
{
     services.AddZipDeploy(c =>
     {
         //指定要忽略的文件或路径(例:"DebugLog.txt"、"Data"、"Log\Today")
         c.IgnorePathStarting("Log");
         c.IgnorePathStarting("Data");
     });
    ······
 }

📒 枚举操作

// 获取枚举对象Key与显示名称的字典
var dict = typeof(枚举类).GetDictionary();
// 获取枚举值对应的字符串
var enumStr = 0.ToEnumString(typeof(枚举类));

📒 服务集合扩展

📖 追踪上下文

按W3C Trace Context 规范, “traceparent” 头部的值应遵循 00-{traceId}-{spanId}-{traceFlags} 的格式

其中:

  • traceId 是一个 32 个十六进制数字的字符串,表示追踪操作的唯一标识符。
  • spanId 是一个 16 个十六进制数字的字符串,表示单个操作的唯一标识符 (例如,一个 HTTP 请求)。
  • traceFlags 是一个 2 个十六进制数字的字符串,表示追踪选项。目前,唯一定义的选项是 “01”,表示记录追踪数据

示例值:

00-83efd447300bf4dfa660e807869e78f0-3f977cb3083d2ac6-01
✏ 将追踪上下文注入到 HTTP 头部
// 将追踪上下文注入到 HTTP 头部
builder.Services.AddTracingDelegatingHandler();

这个方法是用于在分布式服务中注入追踪上下文至 HTTP 头部。

它遵循 W3C Trace Context 规范,通过创建一个 TracingDelegatingHandler 实例,并将其添加到 HttpClient 的消息处理程序中。

这样,每当 HttpClient 发送请求时,都会自动将追踪上下文注入到 HTTP 请求头部。

需要注意的是,在使用HttpClient的时候,要注册名为 “TracingClient” 的 HttpClient


示例:

using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using Microsoft.Extensions.Logging;

namespace YourNamespace.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class YourController : ControllerBase
    {
        private readonly ILogger<YourController> _logger;
        private readonly HttpClient _client;

        public YourController(ILogger<YourController> logger, IHttpClientFactory clientFactory)
        {
            _logger = logger;
            //  注意这里
            _client = clientFactory.CreateClient("TracingClient");
        }

        [HttpGet("get")]
        public async Task<IActionResult> GetAsync()
        {
            var response = await _client.GetAsync("http://example.com/api/values");
            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                return Ok(content);
            }
            else
            {
                return StatusCode((int)response.StatusCode, response.ReasonPhrase);
            }
        }

        [HttpPost("post")]
        public async Task<IActionResult> PostAsync([FromBody] YourModel model)
        {
            var content = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
            var response = await _client.PostAsync("http://example.com/api/values", content);
            if (response.IsSuccessStatusCode)
            {
                var responseContent = await response.Content.ReadAsStringAsync();
                return Ok(responseContent);
            }
            else
            {
                return StatusCode((int)response.StatusCode, response.ReasonPhrase);
            }
        }
    }
}
✏ 设置跟踪上下文中间件

检查上游HTTP追踪上下文,将其设置为当前活动

//设置跟踪上下文中间件
app.UseSetTraceContextMiddleware();

这个方法是用于设置跟踪上下文中间件,它会检查上游 HTTP 追踪上下文,并将其设置为当前活动。

这个中间件首先从 HTTP 请求头部提取追踪上下文,然后创建一个新的 Activity,并将其设置为当前活动。

这样,你就可以在整个请求处理过程中访问和操作这个 Activity。

📖 注册配置文件

只需传入与配置文件对应的实体即可

// 注册配置文件
builder.Services.AddConfiguration<AppSettings>(builder.Configuration, "AppSettings");
//builder.Services.AddConfiguration<配置文件实体>(builder.Configuration, "节点");

这样,AppSettings 类型的配置就会被注册到服务中,你可以在任何需要的地方获取它。

📒时间同步

📖 说明

TimeSyncService 时间同步工具

  • 从多个 NTP 服务器中获取准确时间。
  • 自动选择可用的 NTP 服务器进行同步。
  • 支持 Windows 和 Linux 系统。
  • 提供定时任务,定期自动同步时间。
  • 支持自定义同步时间间隔。

内置了一些常用的 NTP 服务器地址:

  • 0.cn.pool.ntp.org
  • 1.cn.pool.ntp.org
  • 2.cn.pool.ntp.org
  • 3.cn.pool.ntp.org
  • ntp.aliyun.com
  • ntp1.aliyun.com
  • ntp2.aliyun.com
  • ntp3.aliyun.com

📖 使用方法

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"当前时间:\r\n{DateTime.Now:yyyy/MM/dd HH:mm:ss ms}");

        // 执行一次时间同步并启动定时任务
        TimeSyncService.StartAutoSync(6);

        Console.WriteLine("时间同步任务已启动。按Enter键退出...");
        Console.ReadLine();
    }
}

📖 自定义同步时间间隔

默认情况下,TimeSyncService 每隔 6 小时同步一次时间。如果需要自定义同步时间间隔,可以在调用 StartAutoSyncAsync 方法时传入所需的间隔小时数。

例如,每隔 3 小时同步一次:

TimeSyncService.StartAutoSync(3);

📖 主要功能方法

  • GetNtpServerTimeAsync:异步获取指定 NTP 服务器的时间。
  • SetSystemTime:设置系统时间,支持 Windows 和 Linux。
  • StartAutoSyncAsync:启动自动时间同步任务,并设定同步间隔。

📖 注意事项

  • 在 Linux 系统上运行时,需要具有超级用户权限,因为 date -s 命令需要提升的权限。
  • 确保 NTP 服务器列表中的服务器可以访问。

📒域名证书有效期检查

📖 说明

internal class Program
{
    private static async Task Main(string[] args)
    {
        var data = await "https://域名:6666/".CheckDomainExpiryAsync();
        
    }
}

// 域名证书检查结果
public class DomainExpiryResult
{
    public string Url { get; set; }
    public string Status { get; set; }
    public string Subject { get; set; }
    public string Issuer { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
    public double DaysUntilExpiry { get; set; }
    public bool IsExpiringSoon { get; set; }  // 少于30天则表示即将到期
    public string SslPolicyErrors { get; set; }
}

🍀更新日志

3.1.7

【新增】对简单的IO操作,新增了一些方法,简单整理了一个说明示例,优化了一下性能

3.1.6

【新增】Byte扩展

【更新】Yoko.Tool.Security 说明

3.1.5

【新增】域名证书有效期检查

3.1.1

【新增】获取指定月份的所有日期和星期

 var daysWithWeekdays = DateTimeExtensions.GetDaysWithWeekdays(2024, 5);
 // var daysWithWeekdays = DateTimeExtensions.GetDaysWithWeekdays(2024, 5, true);   //获取完整的星期名称
 foreach (var (day, weekday) in daysWithWeekdays)
 {
    Console.WriteLine($"Day: {day}, Weekday: {weekday}");
 }

3.1.0

  • 正式发版

3.0.3-beta.5

【优化】字节数组转16进制字符ByteToString()变更为ByteToHexString

3.0.3-beta.4

【新增】ByteToString() 字节数组转为字符串 ,默认去掉自带的-

3.0.3-beta.3

【优化】HttpClient忽略 SSL 证书验证

3.0.3-beta.2

【优化】优化部分内存泄漏问题

3.0.3-beta.1

【新增】替换主机地址 ReplaceHost

3.0.3

【新增】服务器时间同步工具

【变更】 将在下个版本移除汉明距离,请使用F23.StringSimilarity

3.0.2

【优化】ToModel反序列化时,支持自定义参数

3.0.1

【修复】常规加密组件使用异常

【修复】随机数组件示例错误

【新增】服务集合扩展支持注册配置文件

3.0.0

【新增】服务集合扩展:追踪上下文

  • 将追踪上下文注入到 HTTP 头部
  • 设置跟踪上下文中间件,检查上游HTTP追踪上下文,将其设置为当前活动

2.0.0-beta.1

【注意】部分结构破坏性更新,命名空间变化

【新增】重构的加密算法,并支持部分国密

  • AES,DES,RC4,TripleDES,RSA
  • SM2,SM3,SM4

【新增】部分值类型操作

【新增】字符串相识度。对比字符串的汉明距离,汉明距离是编辑距离中的一个特殊情况,仅用来计算两个等长字符串中不一致的字符个数

【优化】 优化DataTable 转List、 DataTable 转实体性能

1.0.10-rc.4

【修复】修复方法 GetComplementPeriods 出现多余的值问题

1.0.10-rc.3

【新增】获取所有的补集时间段( 默认过滤差值为1min ) GetComplementPeriods

var range = new DateTimeRange(new DateTime(2021, 9, 3, 7, 8, 10)
              , new DateTime(2021, 9, 4, 18, 0, 0));
Console.WriteLine($"时段:{range}");
Console.WriteLine();
Console.WriteLine();
var ranges = new List<DateTimeRange>()
{
    new DateTimeRange(new DateTime(2021, 9, 3, 10, 0, 0), new DateTime(2021, 9, 3, 15, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 3, 20, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 8, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 4, 12, 0, 0), new DateTime(2021, 9, 4, 18, 30, 59)),
    new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
};
var chargingPeriods = range.GetComplementPeriods(ranges, TimeSpan.FromMinutes(1));

foreach (var chargingPeriod in chargingPeriods)
{
    Console.WriteLine($"补集时段:{chargingPeriod}");
}

1.0.10-rc.2

【新增】判断时间段是否与一组时间段相交/包含,并返回所有相交/包含的时间段 IntersectWith

var range = new DateTimeRange(new DateTime(2021, 9, 3, 7, 8, 10)
              , new DateTime(2021, 9, 4, 18, 0, 0));
Console.WriteLine($"时段:{range}");
Console.WriteLine();
Console.WriteLine();
var ranges = new List<DateTimeRange>()
{
    new DateTimeRange(new DateTime(2021, 9, 3, 10, 0, 0), new DateTime(2021, 9, 3, 15, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 3, 20, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 8, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 4, 12, 0, 0), new DateTime(2021, 9, 4, 18, 30, 59)),
    new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
};

var freePeriods = range.IntersectWith(ranges);

foreach (var free in freePeriods)
{
    Console.WriteLine($"相交/包含 时段:{free}");
}

1.0.10-rc.1

【新增】枚举操作:typeof(MyEnum).GetDescriptionItems 获取枚举值和描述的字典映射

枚举值必须携带 [Description("自定义")]

1.0.9

【新增】Net8支持

【优化】使用 AppVersion.Current 时,它将返回入口程序一个 不包含 元数据的版本号

【优化】使用 AppVersions.Current 时,它将返回入口程序一个 包含 元数据的版本号

1.0.7

【新增】递归获取指定路径下的所有文件路径 GetFilePath

1.0.6

【修复】GetStream 异常问题

1.0.5

【新增】将包含逗号分隔的字符串转换为 long 类型的数组:SplitCsvToLong

【新增】Svg相关操作:

  • 判断是否为有效Svg: IsValidSvg
  • SVG过滤器:FilterSvg

【新增】枚举类根据枚举成员获取Display的属性Name:GetDisplay

1.0.4

【优化】雪花id部分项

【新增】获取程序版本号 AppVersion.Current

1.0.3

【新增】查询系统资源信息 (适合Windows和大部分的Linux发行版)

  • SystemInfo.GetLocalIPAddress(); // 获取本机当前正在使用的IP地址
  • SystemInfo.GetAllMacAddresses(); //获取本机所有MAC地址
  • SystemInfo.GetOSVersion(); //获取操作系统版本
  • SystemInfo.GetDriveSpaceInfo(); //获取所有硬盘的可用空间和总空间。单位:GB
  • SystemInfo.GetMemorySpaceInfo(); //获取内存的可用空间和总空间。单位:GB

1.0.2

  • ToJson 新增支持自定义格式化参数 JsonSerializerOptions

    //默认
    JsonSerializerOptions options ??= new()
    {
        //整齐打印
        //options.WriteIndented = true;
        //重新编码,解决中文乱码
        Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
        // 反序列化不区分大小写
        PropertyNameCaseInsensitive = true,
        // 驼峰命名
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        // 对字典的键进行驼峰命名
        DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
        // 忽略只读属性
        //IgnoreReadOnlyProperties = true,
        // 允许在反序列化的时候原本应为数字的字符串(带引号的数字)转为数字
        NumberHandling = JsonNumberHandling.AllowReadingFromString,
    };
    //时间格式化问题
    //参考:https://learn.microsoft.com/zh-cn/dotnet/standard/datetime/system-text-json-support#using-datetimeoffsetparse-and-datetimeoffsettostring
    

1.0.1

  • 精确经纬度计算的方法GetDistance

1.0.0

  • 全面支持Net7

  • 移除 Newtonsoft.Json,全面拥抱 System.Text.Json

  • 移除 仅在 Windows 上支持的 System.Drawing.Common

    跨平台推荐使用 SkiaSharp

    SkiaSharp 是适用于 .NET 平台的跨平台 2D 图形 API,基于 Google 的 Skia 图形库

    它提供了一个全面的 2D API,可以跨移动、服务器和桌面模型使用来渲染图像

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.1.7 71 11/8/2024
3.1.6 72 11/8/2024
3.1.5 78 11/4/2024
3.1.1 116 8/30/2024
3.1.0 103 8/30/2024
3.0.3-beta.5 63 7/25/2024
3.0.3-beta.4 56 7/22/2024
3.0.3-beta.3 54 7/1/2024
3.0.3-beta.2 53 6/27/2024
3.0.3-beta.1 52 6/27/2024
3.0.2 114 5/13/2024
3.0.1 107 5/10/2024
1.0.10-rc.4 92 12/14/2023 1.0.10-rc.4 is deprecated because it is no longer maintained and has critical bugs.
1.0.9 158 11/16/2023
1.0.7 130 10/11/2023
1.0.6 124 10/11/2023
1.0.5 125 9/19/2023
1.0.4 182 5/22/2023
1.0.3 256 2/24/2023
1.0.2 223 2/22/2023
1.0.1 242 2/14/2023
1.0.0 271 11/18/2022