9

SQL Server は、フィールドに 2 バイトの固定長文字エンコーディングであるUnicode UCS-2を使用しているようです。nchar/nvarchar一方、C# は文字列に Unicode UTF-16エンコーディングを使用します (注: UCS-2 を Unicode と見なさない人もいますが、Unicode サブセット 0-0xFFFF で UTF-16 と同じコード ポイントをすべてエンコードし、 SQL Server に関する限り、それは文字列に関してネイティブでサポートされている "Unicode" に最も近いものです。)

UCS-2 は Basic Multilingual Plane (BMP) で UTF-16 と同じ基本コード ポイントをエンコードしますが、サロゲート ペアを許可するために UTF-16 が行う特定のビット パターンを予約しません。

C# 文字列を SQL Server nvarchar(UCS-2) フィールドに書き込んで読み返すと、常に同じ結果が返されますか?

UTF-16 はより多くのコード ポイント (たとえば 0xFFFF より上) をエンコードするという意味で、UTF-16 は UCS-2 のスーパーセットであると思われますが、実際には 2 バイト レベルでの UCS-2 のサブセットです。より制限的です。

私自身の質問に答えるために、C# 文字列に 0xFFFF (文字のペアで表される) を超えるコード ポイントが含まれている場合、これらはデータベースに保存され、取得されると思われますが、データベースでそれらを操作しようとすると (たとえば、おそらく TOUPPER を呼び出すか、1 文字おきに空白にしようとしている可能性があります)、後で文字列を表示する際に問題が発生する可能性があります... SQL Server にサロゲート ペアを認識し、nchar/nvarchar文字列を UTF-16 として効果的に処理する関数がない限り。

4

2 に答える 2

4

テキストを UCS-2 として扱うことが多くの問題を引き起こすとは思いません。

(私の知る限り) BMP の上に大文字と小文字のマッピングはなく (もちろん ID マッピングを除いて!)、明らかに、代理文字はそれ自体にマッピングされるため、大文字と小文字の変換は問題になりません。

他のすべての文字を空白にすることは、トラブルを求めているだけです. 実際には、キャラクターの価値を考慮せずにこの種の変換を行うことは、常に危険な行為です。文字列の切り捨てで合法的に起こっていることがわかります。しかし、一致しないサロゲートが結果に現れたとしても、それ自体は大きな問題ではありません。そのようなデータを受信し、気にかけているシステムは、おそらく、一致しないサロゲートを代わりの文字に置き換えるだけです。

明らかに、文字列の長さは文字数ではなくバイト/2 になりますが、Unicode コード チャートの深さを調べ始めると、とにかく文字数はあまり有用な値ではありません。たとえば、文字、RTL 言語、方向制御文字、タグ、およびいくつかの種類のスペース文字の組み合わせにより、ASCII 範囲を離れると等幅表示で良い結果が得られません。高いコードポイントは、問題の中で最も少なくなります。

念のため、楔形文字テキストは考古学者の名前とは別の列に保存する必要があります。:D

経験的データで今すぐ更新してください!

ケース変換で何が起こるかを確認するためのテストを実行しました。英単語 TEST を大文字で 2 回使用した文字列を作成しました。最初はラテン文字、次に 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 フォントがインストールされている場合に備えて、実際の文字列を以下に示します。

  Original: TEST
.NET Lower: test
  DB Lower: test
于 2011-04-13T20:49:09.547 に答える
4

それは本当にすべてファッジです。

まず類似点

  • SQL Server の //ncharデータnvarcharntextは、テキストを 2 バイト文字の文字列として格納します。検索と並べ替えを行うまでは、何を入力してもかまいません (その後、適切な Unicode 照合シーケンスが使用されます)。
  • CLRStringデータ型は、テキストを 2 バイトCharの文字列として格納することもできます。また、検索と並べ替えを行うまでは、何を入力してもかまいません (その後、適切なカルチャ固有のメソッドを使用します)。

今違い

  • .NET では、 StringInfoクラスを介して CLR 文字列内の実際の Unicode コード ポイントにアクセスできます。
  • .NET は、さまざまなエンコーディングでテキスト データをエンコードおよびデコードするためのサポートを多数備えています。任意のバイト ストリームを に変換する場合、String常に文字列を UTF-16 としてエンコードします (多言語プレーンを完全にサポートします)。

つまり、CLR と SQL Server の両方の文字列変数をテキストの塊全体として扱う限り、情報を失うことなく一方から他方へ自由に割り当てることができます。最上位層の抽象化がわずかに異なっていても、基本的なストレージ形式はまったく同じです。

于 2011-04-13T20:48:41.497 に答える