1

SQL nchar から char への .NET キャスト/変換が必要です。
より具体的には、nchar UNICODE を char ASCII にキャストします。

これが複雑なのは、SQL char が完全なバイトを使用することです。
128 の純粋な ASCII ではありません
。TSQL 関数 ASCII は 0 ~ 255 を返します。

FormByte の NormalizationForm が存在することが理想的です。
正確なテキスト値ではなく、近い論理値または ? になります。
また、SQL は FormByte を使用して nchar から char にキャストします。
正規化フォーム

エンコード デコードがうまくいかず、すべてのフレーバーを試しました。

SQL では、多くの char (バイト) が 63 にマップされます。63 は ? です。
63 にマップされる 255 を超える char だけではありません
。130 から 140 はすべて 63 にマップされます。

文字 160 ~ 255 はすべて 160 ~ 255 を返します

255 を超えるすべてが 63 にマップされるわけではありません。
たとえば、多くの分音記号は ASCII にマップされます。

TSQL には、UNICODE および ACSII 関数があります。
したがって、すべての Unicode 文字を char 列と nchar 列の両方にロードしました。

SQL によって返される char は、29 文字に対して間違っています。
また、不正な文字に対して返される ASCII() は意味がありません。すべての制御文字は 130 ~ 160 の範囲です。
バイナリで不正な 29 をチェックしたところ、格納されているのは ASCII() によって返されたものです。
27 の場合、char から返されるのは nchar であり、2 の場合は正しい nchar ではありません。それらはすべて ? にマップする必要があります。または同等の ACSII。 “ と ” は"
にマッピングされますが、
?

私はあなたが私を信じていないことを知っています。
「Œ」を char 列に挿入して選択すると、「Œ」が返されます。
また、それを検索することもできます - char = 'Œ' は true を返します。
Select ASCII('Œ') は 140 を返し、それが実際に格納されているものです (バイナリを確認してください)。
140 / 8C の UNICODE 定義は、Partial Line Backward です。
その文字のバイナリ値を確認したところ、8C (140) でした。
返されるのは Unicode 'Œ' Int16 338
です。SQL が何らかの入出力マッピングを行っていて、それが間違っているようです。

ASCII 関数は、? にマップされていない 575 個の Unicode 文字に対して正しいです。
char 値は ACSII と一致し、すべて意味があります。
EG 12 の異なる形式の u はすべて u にマップされます。
? 以外の 32163 文字 にマッピングされていますか? (63)。

以下は、間違った値を返す 29 文字です。
列の順序:
char
nchar
ASCII(char)
UNICODE(nchar)

     sqlCharASCIIbackToString did not match  Œ Œ 140 338
     sqlCharASCIIbackToString did not match  œ œ 156 339
     sqlCharASCIIbackToString did not match  Š Š 138 352
     sqlCharASCIIbackToString did not match  š š 154 353
     sqlCharASCIIbackToString did not match  Ÿ Ÿ 159 376
     sqlCharASCIIbackToString did not match  Ž Ž 142 381
     sqlCharASCIIbackToString did not match  ž ž 158 382
     sqlCharASCIIbackToString did not match  ƒ Ƒ 131 401
     sqlCharASCIIbackToString did not match  ƒ ƒ 131 402
     sqlCharASCIIbackToString did not match  ˆ ˆ 136 710
     sqlCharASCIIbackToString did not match  ˜ ˜ 152 732
     sqlCharASCIIbackToString did not match  – – 150 8211
     sqlCharASCIIbackToString did not match  — — 151 8212
     sqlCharASCIIbackToString did not match  ‘ ‘ 145 8216
     sqlCharASCIIbackToString did not match  ’ ’ 146 8217
     sqlCharASCIIbackToString did not match  ‚ ‚ 130 8218
     sqlCharASCIIbackToString did not match  “ “ 147 8220
     sqlCharASCIIbackToString did not match  ” ” 148 8221
     sqlCharASCIIbackToString did not match  „ „ 132 8222
     sqlCharASCIIbackToString did not match  † † 134 8224
     sqlCharASCIIbackToString did not match  ‡ ‡ 135 8225
     sqlCharASCIIbackToString did not match  • • 149 8226
     sqlCharASCIIbackToString did not match 
     … … 133 8230
     sqlCharASCIIbackToString did not match  ‰ ‰ 137 8240
     sqlCharASCIIbackToString did not match  ‹ ‹ 139 8249
     sqlCharASCIIbackToString did not match  › › 155 8250
     sqlCharASCIIbackToString did not match  € € 128 8364
     sqlCharASCIIbackToString did not match  ™ ™ 153 8482
     sqlCharASCIIbackToString did not match  ˜ ≈ 152 8776
     count63 =  32163 countMis =  29 countCorrect =  575

次の .NET を実行して、SQL によって返された場合にどの「Œ」が返されるかを確認しました

char char338 = (char)338;
System.Diagnostics.Debug.WriteLine(char338);
sqlCmd.CommandText = "select [char] from [charNchar] where [char] = @char;";
sqlCmd.Parameters.Add("@char", SqlDbType.Char).Value = char338;
string string338= sqlCmd.ExecuteScalar().ToString();
char338 = string338.ToCharArray()[0];
System.Diagnostics.Debug.WriteLine(char338 + " " + ((Int16)char338).ToString());

上記のコードは Œ 338 を返し
ます。SQL は、byte として格納されているはずのデータ型に byte より大きい値を返しています。
(char)140 で検索すると、? 63 が返されます。

興味深いのは、'ā' と char の N'ā' で検索すると、異なる結果が得られることです。
それは左側の検索です (140) Œ。
右側を検索 (338) Œ char 検索では何も見つかりません。
Nchar は、どちらかの入力で両方の結果を見つけます。

  SELECT [int16],RTRIM([char]) as [char], ASCII([char]) as 'ASCII'
                ,RTRIM([nchar]) as [nchar], UNICODE([nchar]) as 'UNICODE'
  FROM [test].[dbo].[charNchar]
  where [char] = 'Œ'
  SELECT [int16],RTRIM([char]) as [char], ASCII([char]) as 'ASCII'
                ,RTRIM([nchar]) as [nchar], UNICODE([nchar]) as 'UNICODE'
  FROM [test].[dbo].[charNchar]
  where [char] = N'Œ'
  SELECT [int16],RTRIM([char]) as [char], ASCII([char]) as 'ASCII'
                ,RTRIM([nchar]) as [nchar], UNICODE([nchar]) as 'UNICODE'
  FROM [test].[dbo].[charNchar]
  where [nchar] = 'Œ'
  SELECT [int16],RTRIM([char]) as [char], ASCII([char]) as 'ASCII'
                ,RTRIM([nchar]) as [nchar], UNICODE([nchar]) as 'UNICODE'
  FROM [test].[dbo].[charNchar]
  where [nchar] = N'Œ'


int16  char                                               ASCII       nchar                                              UNICODE
------ -------------------------------------------------- ----------- -------------------------------------------------- -----------
338    Œ                                                  140         Œ                                                  338

int16  char                                               ASCII       nchar                                              UNICODE
------ -------------------------------------------------- ----------- -------------------------------------------------- -----------
338    Œ                                                  140         Œ                                                  338
339    œ                                                  156         œ                                                  339

int16  char                                               ASCII       nchar                                              UNICODE
------ -------------------------------------------------- ----------- -------------------------------------------------- -----------
338    Œ                                                  140         Œ                                                  338
339    œ                                                  156         œ                                                  339

int16  char                                               ASCII       nchar                                              UNICODE
------ -------------------------------------------------- ----------- -------------------------------------------------- -----------
338    Œ                                                  140         Œ                                                  338
339    œ                                                  156         œ                                                  339

≈ search は、4 つのクエリのいずれでも何も見つかりません。チャートを確認したところ、これは 8776 の正しい文字であり、数学的にはほぼ同じです。 

〜はSSMSに貼り付けられたゼロ幅ですが、FROMに貼り付けられたかのように、青が黒に変わります。 

私は何かが足りないのですか - これは私にはバグのようです。
値が間違っているだけでなく、無効な値です。
Int16 が返されます。
スペースを節約するために文字を格納するためにバイトを使用したいとしましょう.29文字がバイトとして返されないため、SQL文字で壊れます。

使用したコードは次のとおりです。

public void SQLchar()
{

    SqlConnection sqlCon = new SqlConnection(connString);  
    try
    {         
        sqlCon.Open();
        SqlCommand sqlCmd = sqlCon.CreateCommand();
        SqlDataReader rdr;
        sqlCmd.CommandText = "delete charNchar";
        sqlCmd.ExecuteNonQuery();
        for(Int16 i = 0; i < Int16.MaxValue; i ++)
        {
            sqlCmd.CommandText = "insert into charNchar (int16,char,nchar) values (@int16, @char, @nchar);";
            sqlCmd.CommandType = System.Data.CommandType.Text;
            sqlCmd.Parameters.Clear();
            sqlCmd.Parameters.Add("@int16", SqlDbType.Int).Value = i;
            sqlCmd.Parameters.Add("@char", SqlDbType.Char).Value = (char)i;
            sqlCmd.Parameters.Add("@nchar", SqlDbType.NChar).Value = (char)i;
            sqlCmd.ExecuteNonQuery();
        }
        string sqlChar;
        string sqlNChar;
        Int16 sqlCharASCII;
        Int16 sqlNCharUnicode;
        string sqlCharASCIIbackToString;
        sqlCmd.CommandText = "select char,nchar,ASCII(char),UNICODE(nchar) from charNchar order by int16;";
        rdr = sqlCmd.ExecuteReader();
        Int16 count63 = 0;
        Int16 countMis = 0;
        Int16 countCorrect = 0;
        while (rdr.Read())
        {
            sqlChar = rdr.IsDBNull(0) ? "dbNull" : rdr.GetString(0);
            sqlNChar = rdr.IsDBNull(1) ? "dbNull" : rdr.GetString(1);
            sqlCharASCII = rdr.IsDBNull(2) ? Int16.Parse("-1") : (Int16)rdr.GetInt32(2);
            sqlNCharUnicode = rdr.IsDBNull(3) ? Int16.Parse("-1") : (Int16)rdr.GetInt32(3);
            if(sqlCharASCII == 63 && sqlNCharUnicode != 63)
            {
                count63 ++;
                continue;  // ?
            }
            if (sqlCharASCII < 0)
            {
                System.Diagnostics.Debug.WriteLine("ASCII(char) null for " + sqlChar + " " + sqlNChar);
            }
            else
            {
                sqlCharASCIIbackToString = ((char)sqlCharASCII).ToString();
                if (string.CompareOrdinal(sqlChar, sqlCharASCIIbackToString) != 0)
                {
                    countMis++;
                    System.Diagnostics.Debug.WriteLine(" sqlCharASCIIbackToString did not match " + sqlCharASCIIbackToString + " " + sqlChar + " " + sqlNChar + " " + sqlCharASCII + " " + sqlNCharUnicode);
                }
                else
                {
                    countCorrect++;
                }
            }
        }
        rdr.Close();
        System.Diagnostics.Debug.WriteLine("count63 =  " + count63.ToString() + " countMis =  " + countMis.ToString() + " countCorrect =  " + countCorrect.ToString());
    }
    catch (Exception Ex)
    {
        System.Diagnostics.Debug.WriteLine(Ex.Message);
    }
    finally 
    {
        sqlCon.Close();
    }
}

理由は。
.NET で文字列データを解析し、そのデータは FK です。
FK の ID を取得するために SQL に往復するのではなく、速度のために .NET ディクショナリを使用します。
Dictionary は、値からキーを取得するための逆引きです。
パーサーは、既にパーサーによって使用されているため、char の Int16 を持っています。
そのため、char の ASCII が間違っている場合、逆引きは失敗します。
誤った ASCII 結果の修正をハードコーディングできると思います。
しかし、パッチから始まる道をたどる前に、ここで何が起こっているのかを理解したい.
char には根本的な欠陥がありますか?
nchar を使用することもできますが、char を使用することをお勧めします。
アプリケーションの性質は、一致が必要です。
u の 6 つの分音符号がすべて ascii u に一致するのは良いことです。

4

3 に答える 3

2

したがって、ここで、あなたがやろうとしているのは、Unicode データを取得してデータベースの varchar フィールドに格納しているという事実を悪用することだと仮定します...入力エンコーディングをアスキー文字列

string bad = Encoding.ASCII.GetString(Encoding.Unicode.GetBytes(input));
于 2013-04-13T01:37:51.157 に答える
0

ローカル システムまたは SQL サーバー システムのいずれかで使用されているコード ページに関連する変換の異常に遭遇していると推測できます (これは、いわゆる "High-ASCII" 10 進数の 128 ~ 255 文字がどのように解釈されるかに影響します)。および列/テーブル/データベースで使用されている照合。SQL Server は、使用中のコード ページと照合順序に基づいて、「無効な」文字を有効な CHAR に変換しようとします。

ヨーロッパのクライアントが、ヨーロッパ コード ページがアクティブなシステムでセットアップされたファイルを使用して、'ü' を含む名前を読み込もうとしたときに問題が発生しました。コード ページ 437 をアクティブにした SQL Server セットアップ (OEM CP 設定、IIRC) では、'÷' が格納され、返されました。(10 進数 246) CHAR データのコード ページの問題が解決されると、すべてがうまくいきませんでした。入力と出力のセットアップの詳細は覚えていません。申し訳ありません。

編集:このStackoverflow の記事では、「?」を含むいくつかの問題についてかなり詳しく説明しています。もの。

于 2013-04-15T19:50:47.217 に答える