以下 C# 方法考虑组合字符(Grapheme Clusters)来计算字符串字符数。这是:
public static class StringExtensions
{
public static SqlInt32 GetStrLength(this string input)
{
if (string.IsNullOrEmpty(input))
return 0;
return StringInfo.ParseCombiningCharacters(input).Length;
}
}
现在,我从中创建一个 CLR 以在 SQL Server 内部使用。这是它的代码:
public static class UserDefinedFunctions
{
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlInt32 GetStrLength(SqlString input)
{
if (input.IsNull)
return 0;
return StringInfo.ParseCombiningCharacters(input.Value).Length;
}
}
C# 版本运行良好,但在 SQL Server 中,无法正常计数。有什么问题吗?
以下是 SQLCLR 函数无法正确计数的几个示例:
SQLCLR 版本(错误) | 非 SQLCLR 版本(正确) |
---|---|
'👩🏻' -> 2 | '👩🏻' -> 1 |
'👨🏻u200d❤️u200d💋u200d👩🏼' -> 9 | '👨🏻u200d❤️u200d💋u200d👩🏼' -> 1 |
这是我为获取长度而运行的 SQL 代码:
SELECT dbo.GetStringLength(body) FROM notes;
以下是用于注册SQLCLR的SQL代码:
EXEC sp_configure 'show advanced options' , 1;
RECONFIGURE;
EXEC sp_configure 'clr enable' ,1;
RECONFIGURE;
EXEC sp_configure 'clr strict security', 0;
RECONFIGURE;
CREATE ASSEMBLY StringUtils FROM 'C:\GraphemeClusters.dll' WITH PERMISSION_SET = SAFE;
CREATE FUNCTION dbo.GetStringLength(@input NVARCHAR(MAX))
RETURNS INT
AS EXTERNAL NAME StringUtils.UserDefinedFunctions.GetStrLength;
数据库的排序规则并不重要,因为
_SC
和 _140_
排序规则仅真正影响内置函数的行为,并且仅与增补字符相关。
我在 SQL Server 2017 和 2022 中使用以下测试字符串测试了您的
GetStrLength
方法以及 StringInfo.LengthInTextElements
:
DECLARE @Input NVARCHAR(50) =
-- first two count individually as they do not have a base character
NCHAR(0x0303) + NCHAR(0x0303) +
-- next character counts as it is a base character
N'o' +
-- next four do not count as they all attach to the base character
NCHAR(0x0303) + NCHAR(0x0302) + NCHAR(0x0303) + NCHAR(0x0302);
并且在所有情况下都返回了预期值 3(“0x0303”和“0x0302”都是组合字符)。
问题在于 SQL Server 与 .NET Framework 绑定在一起,并且最高版本 4.8 不处理表情符号“序列”。然而,较新版本的 .NET 可以。因此,虽然您认为正在针对 .NET Framework 4.8 测试非 SQLCLR 版本,但实际上您正在针对更新版本的 .NET 进行测试。
我在 LINQPad 版本 5 和 8 中执行了以下操作(OP 提供的测试表情符号序列,减去 U+1F3FB 的肤色修饰符,可以在 Unicode 表情符号文档 中找到,作为“多人”性别”示例):
string smooch =
"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB";
System.Console.WriteLine(smooch);
System.Console.WriteLine(
System.Globalization.StringInfo.ParseCombiningCharacters(smooch)
.Length);
与 .NET Framework 4.7 和 4.8 配合使用的 LINQPad 5 返回:
👩🏻❤️💋👨🏻
9
与 .NET 5 到 9 兼容的 LINQPad 8 返回(我针对 .NET 6.0.35 和 8.0.10 进行了测试):
👩🏻❤️💋👨🏻
1
(稍后我将用每个表情符号的输出图片进行更新,因为虽然 LINQPad 8 的输出与上面看到的相同,但 LINQPad 5 的输出显示 4 个不同的表情符号——女人、心、嘴唇和男人—— - 使用他们的文本演示而不是单个字形)