using ClockSnowFlake;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace dy.net.utils
{
///
/// 抖音标题转文件名工具类(兼容Windows/macOS/Linux)
///
public static class DouyinFileNameHelper
{
///
/// 处理文件名/文件夹名,确保符合 Linux 限制(最大 255 字节 UTF-8 编码)
///
/// 原始名称(支持英文、中文、混合字符)
/// 截取后为空时的默认名称(默认 "default")
/// 符合 Linux 规则的合法名称
public static string SanitizeLinuxFileName(string originalName, string defaultName = "default")
{
// 1. 空值处理:直接返回默认名
if (string.IsNullOrWhiteSpace(originalName))
return defaultName.Replace(" ","");
// 2. 过滤 Linux 非法字符:
// - 禁止:/(路径分隔符)、\0(空字符)
// - 替换:其他特殊字符(如 :*?"<>|\\ )为下划线 _,避免创建失败
var invalidChars = new[] { '/', '\0', ':', '*', '?', '"', '<', '>', '|', '\\' };
string sanitizedName = originalName;
foreach (var c in invalidChars)
{
sanitizedName = sanitizedName.Replace(c, '_');
}
// 3. 计算 UTF-8 字节数,若未超 255 字节,直接返回
byte[] utf8Bytes = Encoding.UTF8.GetBytes(sanitizedName);
if (utf8Bytes.Length <= 252)
return sanitizedName.Replace(" ", ""); ;
// 4. 超过 255 字节,截取前 255 字节(避免破坏 UTF-8 字符)
byte[] truncatedBytes = new byte[252];
Array.Copy(utf8Bytes, truncatedBytes, 252);
// 5. 字节数组转回字符串(自动忽略不完整的尾部字节,避免乱码)
string truncatedName = Encoding.UTF8.GetString(truncatedBytes).TrimEnd('\0').Replace(" ",""); // 移除可能的空字符
// 6. 极端情况:截取后为空(如全是非法字符替换后无有效内容),返回默认名
return string.IsNullOrWhiteSpace(truncatedName) ? defaultName : truncatedName;
}
/// 检查字符串是否仅包含字母、数字、简体中文(无特殊字符)
///
/// 待检查的字符串
/// true:无特殊字符(仅允许字符);false:含有特殊字符
public static bool IsValidWithoutSpecialChars(string input)
{
// 空字符串默认返回 true(若需禁止空字符串,可先判断 string.IsNullOrWhiteSpace(input) 并返回 false)
if (string.IsNullOrEmpty(input))
return true;
// 正则表达式说明:
// ^ :匹配字符串开头
// $ :匹配字符串结尾
// [a-zA-Z0-9\u4E00-\u9FA5] :允许的字符范围
// a-zA-Z:大小写字母
// 0-9:数字
// \u4E00-\u9FA5:简体中文 Unicode 核心范围(覆盖99%+简体中文常用字)
// * :匹配 0 个或多个允许的字符(若需至少1个字符,可改为 +)
const string pattern = @"^[a-zA-Z0-9\u4E00-\u9FA5]*$";
// 忽略文化差异,仅按字符编码匹配
return Regex.IsMatch(input, pattern, RegexOptions.None);
}
}
}