在SQL Server nvarchar(UCS-2)列中存储C#字符串(UTF-16)有什么后果?

| 似乎SQL Server对“ 0”字段使用Unicode UCS-2(2字节固定长度字符编码)。同时,C#的字符串使用Unicode UTF-16编码(注意:有些人不认为UCS-2是Unicode,但是它在Unicode子集0-0xFFFF中编码与UTF-16相同的所有代码点,并且就SQL Server而言,这是它本来支持的字符串“ \ Unicode \”最接近的东西。) 尽管UCS-2在基本多语言平面(BMP)中编码与UTF-16相同的基本代码点,但它并没有保留UTF-16允许的代理对的某些位模式。 如果将C#字符串写入SQL Server
nvarchar
(UCS-2)字段并将其读回,这将始终返回相同的结果吗? 从UTF-16编码更多代码点(例如高于0xFFFF)的意义上讲,UTF-16似乎是UCS-2的超集,但实际上它是2字节级别的UCS-2的子集,因为它更具限制性。 为了回答我自己的问题,我怀疑如果我的C#字符串包含高于0xFFFF的代码点(用字符对表示),这些代码点将在数据库中很好地存储和检索,但是如果我尝试在数据库中进行操作(例如,也许调用TOUPPER或尝试清除所有其他字符),那么以后可能会在显示字符串时遇到一些问题...除非SQL Server具有可识别代理对并将有效的“ 0”字符串视为UTF-16的函数。     
已邀请:
真的有点忽悠。 首先的相似之处 SQL Server的“ 3” /“ 1” /“ 5”数据类型将文本存储为2字节字符的字符串。在您进行搜索和排序之前,它并不真正关心您输入的内容(然后使用适当的Unicode排序顺序)。 CLR“ 6”数据类型还将文本存储为2字节“ 7”的字符串。在您进行搜索和排序之前,它也并不真正关心您输入的内容(然后使用适当的特定于区域性的方法)。 现在的差异 .NET允许您通过StringInfo类访问CLR字符串中的实际Unicode代码点。 .NET具有大量的支持,可以以多种编码方式对文本数据进行编码和解码。将任意字节流转换为“ 6”时,它将始终将字符串编码为UTF-16(具有完整的多语言平面支持)。 简而言之,只要将CLR和SQL Server字符串变量都视为完整的文本块,那么就可以在不损失任何信息的情况下将一个变量自由分配给另一个。基本存储格式完全相同,即使最顶层的抽象略有不同。     
我不希望将文本视为UCS-2会引起很多问题。 大小写转换应该不是问题,因为(AFAIK)BMP上方没有大小写映射(当然,除了身份映射!),而且显然,替代字符也将映射到自己。 遮盖其他所有字符只是在麻烦。实际上,在不考虑字符值的情况下进行这类转换始终是危险的活动。我可以看到它确实发生了字符串截断。但是,如果结果中出现无与伦比的替代指标,这本身并不是一个大问题。任何接收到此类数据并关心的系统都可能会用替换字符替换不匹配的代理,如果它根本不愿意为此做任何事情。 显然,字符串长度将为字节/ 2,而不是字符数,但是一旦您开始研究Unicode代码表的深度,字符数就不是一个非常有用的值。例如,一旦离开ASCII范围,就不会在等宽显示中获得良好的结果,这是因为将字符,RTL语言,方向控制字符,标签和多种空格字符组合在一起。高代码点将是最少的问题。 为了安全起见,您可能应该将楔形文字存储在与考古学家姓名不同的列中。 :D 立即更新经验数据! 我只是进行了测试,以了解大小写转换会发生什么。我用英文大写字母TEST两次创建了一个字符串,首先是拉丁字母,然后是Deseret脚本。我在.NET和SQL Server中对此字符串应用了小写转换。 .NET版本正确地将两个脚本中的所有字母都小写。 SQL Server版本仅将小写的拉丁字母保留为小写,而对Deseret的保留不变。这符合有关处理UTF-16和UCS-2的期望。
using System;
using System.Data.SqlClient;

class Program
{
    static void Main(string[] args)
    {
        string myDeseretText = \"TEST\\U00010413\\U00010407\\U0001041D\\U00010413\";
        string dotNetLower = myDeseretText.ToLower();
        string dbLower = LowercaseInDb(myDeseretText);

        Console.WriteLine(\"  Original: {0}\", DisplayUtf16CodeUnits(myDeseretText));
        Console.WriteLine(\".NET Lower: {0}\", DisplayUtf16CodeUnits(dotNetLower));
        Console.WriteLine(\"  DB Lower: {0}\", DisplayUtf16CodeUnits(dbLower));
        Console.ReadLine();
    }

    private static string LowercaseInDb(string value)
    {
        SqlConnectionStringBuilder connection = new SqlConnectionStringBuilder();
        connection.DataSource = \"(local)\";
        connection.IntegratedSecurity = true;
        using (SqlConnection conn = new SqlConnection(connection.ToString()))
        {
            conn.Open();
            string commandText = \"SELECT LOWER(@myString) as LoweredString\";
            using (SqlCommand comm = new SqlCommand(commandText, conn))
            {
                comm.CommandType = System.Data.CommandType.Text;
                comm.Parameters.Add(\"@myString\", System.Data.SqlDbType.NVarChar, 100);
                comm.Parameters[\"@myString\"].Value = value;
                using (SqlDataReader reader = comm.ExecuteReader())
                {
                    reader.Read();
                    return (string)reader[\"LoweredString\"];
                }
            }
        }
    }

    private static string DisplayUtf16CodeUnits(string value)
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();

        foreach (char c in value)
            sb.AppendFormat(\"{0:X4} \", (int)c);
        return sb.ToString();
    }
}
输出:
  Original: 0054 0045 0053 0054 D801 DC13 D801 DC07 D801 DC1D D801 DC13
.NET Lower: 0074 0065 0073 0074 D801 DC3B D801 DC2F D801 DC45 D801 DC3B
  DB Lower: 0074 0065 0073 0074 D801 DC13 D801 DC07 D801 DC1D D801 DC13
以防万一有人安装了Deseret字体,以下是您真正喜欢的字符串:   原文:TEST

要回复问题请先登录注册