2011-04-13 16 views
8

SQL Server, nchar/nvarchar alanları için 2 baytlık sabit uzunlukta karakter kodlaması olan Unicode UCS-2 kullanır. Bu arada, C#, dizeleri için kodlayan Unicode UTF-16 kullanır (not: Bazı insanlar UCS-2'yi Unicode olarak düşünmez, ancak Unicode alt kümesindeki 0-0xFFFF'deki tüm kod noktalarını UTF-16 olarak kodlar. SQL Server söz konusu olduğunda, bu, "Unicode" karakterinin, karakter dizileri açısından doğal olarak desteklediği en yakın şeydir.)Bir C# dizesinin (UTF-16) bir SQL Server nvarchar (UCS-2) sütununda saklanmasının sonuçları nelerdir?

UCS-2, Temel Çok Dilli Düzlemde UTF-16 ile aynı temel kod noktalarını kodlar. (BMP), UTF-16'nın vekil çiftlere izin vermesi için belirli bit düzenlerini rezerve etmez.

Bir SQL Server'a nvarchar (UCS-2) alanına bir C# dizesi yazıp geri okursam, bu her zaman aynı sonucu döndürür mü?

UTF-16, UTF-16'nın daha fazla kod noktasını (örn. 0xFFFF) kodlaması anlamında UCS-2'nin bir üst kümesi olsa da, aslında 2 baytlık bir UCS-2 alt kümesidir. Seviye, daha kısıtlayıcı olduğu için.

Kendi soruyu yanıtlamak için , benim C# dizesi 0xFFFF (karakter çiftleri tarafından temsil edilen) üzerinde kod noktaları içeriyorsa, bunlar veritabanında saklanıp geri alınabilir, ancak bunları, veritabanını (örneğin belki de TOUPPER'ı çağırmak ya da diğer karakterleri boşaltma girişiminde bulunmak), sonra SQL Server'ın çiftleri kabul eden ve nchar/nvarchar dizeleri UTF-16 olarak etkin bir şekilde tedavi eden işlevleri varsa, daha sonra dizeyi görüntüleyen bazı sorunların üstesinden gelebilirim. .

cevap

3

Bu gerçekten biraz şekerleme.

İlk benzerlikler

  • SQL 2 baytlık karakter dizesi olarak Sunucusu nchar/nvarchar/ntext veri türleri mağaza metni. Arama yapmak ve sıralamak için gelene kadar onlara koyduğunuz şeyi umursamaz (o zaman uygun Unicode harmanlama dizisini kullanır).
  • CLR String veri türü, metni 2 baytlık bir Char s dizesi olarak da depolar. Ayrıca, arama yapmak ve sıralamak için gelene kadar içine koyduğunuz şeyi gerçekten önemsemez (o zaman uygun kültüre özgü yöntemleri kullanır). Şimdi

farklılıklar

  • .NET Eğer StringInfo sınıfa üzerinden bir CLR dizede fiili Unicode kod noktalarını erişmesini sağlar.
  • .NET, çeşitli kodlamalarda metin verilerini kodlama ve kod çözme konusunda çok miktarda desteğe sahiptir. Bir rasgele bayt akışını String'a dönüştürürken, dizeyi her zaman UTF-16 olarak kodlar (tam çok dilli düzlem desteğiyle).

Kısacası, sürece metnin bütün kütleleri olarak hem CLR ve SQL Server dize değişkenleri tedavi olarak, o zaman serbestçe bilgi kaybı olmadan birinden diğerine atayabilirsiniz. Üstte yer alan soyutlamalar biraz farklı olsa bile, temeldeki depolama formatı tamamen aynıdır.

+0

Tamam, okuma/yazma bir dize olarak Bir nvarchar alanına bütün bir varlık, vekil çift olarak yorumlanabilecekleri içeriyor olsa bile, sorunlara veya bilgi kaybına neden olmaz. Şimdi, bir char sütununa C# dizesi yazmaktan ne haber? WOULD'un bazı yorum ve dönüşümleri içerdiğini ve veri kaybına neden olacağından şüpheleniyorum ... – Triynko

+0

Tek baytlık sütunlar, üzerlerinde tanımlanmış Unicode olmayan bir harmanlama dizisine sahiptir, bunlar yalnızca arama ve sıralama kurallarını tanımlamakla kalmaz; karakterlere izin verilir. Sütunun kod sayfasındaki bir değerle eşlenen herhangi bir Unicode kod noktası korunacak ve geri kalanlar atılacaktır. –

+0

Atıldı veya belirli bir kukla veya "karaktersiz" bayt ile değiştirildi mi? Tek baytlık kod sayfaları, karakter olmayanlar için belirli bir bayt ayırır mı? Hedef kod alanında tanımlanmayan Unicode karakterlerinin soru işareti ile değiştirildiğini gösteren bazı örnekler gördüm, ancak belki de karakterlerin nasıl görüntülenmediğini gösteriyor mu? – Triynko

4

Metnin UCS-2 olarak ele alınmasının birçok soruna neden olacağını umuyorum.

Vaka dönüştürmeleri bir sorun olmamalıdır, çünkü (AFAIK) BMP'nin üzerinde (eşlem eşlemesi hariç) hiçbir vaka eşlemesi yoktur ve açıkça, vekil karakterler kendileriyle eşleşeceklerdir.

Diğer tüm karakterleri kapatmak yalnızca sorun çıkarmak içindir. Gerçekte, karakter değerlerini dikkate almadan bu tür dönüşümleri yapmak her zaman tehlikeli bir faaliyettir. Bunu dize kesikleriyle meşru olarak görebiliyorum. Ancak, sonuçta herhangi bir eşleşmemiş vekil görünmüyorsa, bu, büyük sorun değildir. Böyle verileri -veya gözeten- alabilen herhangi bir sistem muhtemelen eşi görülmemiş bir vekil ile yer değiştirecekse, bir ikame karakteri ile değiştirecektir.

Açıkçası, dize uzunluğu, karakter sayısı yerine bayt/2 olacak, ancak Unicode kod çizelgelerinin derinliklerine su eklemeye başladığınızda, karakter sayısı zaten çok kullanışlı bir değer değil. Örneğin, karakterleri, RTL dillerini, yön denetim karakterlerini, etiketleri ve birkaç boşluk karakterini birleştirdiğinizden ASCII aralığından çıktıktan sonra tek aralıklı ekranda iyi sonuçlar almayacaksınız. Yüksek kod noktaları problemlerinizin en azı olacak.

Sadece güvenli tarafta olmak için, çivi yazılı metinleri muhtemelen arkeologların adlarından farklı bir sütunda saklamanız gerekir. : D

Şimdi UPDATE ampirik verilerle!

Sadece durum dönüştürmelerinde neler olduğunu görmek için bir test yaptım. İki kez büyük harfle ingilizce kelime TEST ile bir dizi oluşturdum - önce Latin alfabesinde, sonra Deseret komut dosyasında. .NET ve SQL Server'da bu dizeye küçük harfli bir dönüşüm uyguladım.

.NET sürümü, her iki komut dosyasındaki tüm harfleri doğru bir şekilde küçültüyor. SQL Server sürümü yalnızca Latin karakterlerini indirdi ve Deseret karakterlerini değiştirmedi. Bu, UTF-16 ayetlerinin UCS-2'nin ele alınmasıyla ilgili beklentileri karşılıyor.

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(); 
    } 
} 

Çıktı:

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 

vaka Herkes bir Deseret yazı yüklediği Hemen burada, keyifli zaman gerçek görürsün:

Original: TEST 
.NET Lower: test 
    DB Lower: test 
+0

Yanıt için teşekkürler. Dava dönüştürmelerinin bir sorun olmayacağına katılmıyorum. Örneğin, veritabanındaki bir dizgede TOUPPER çağrılması, bir temsilci çifti varsa, TQL TOUPPER, her bir 2 bayt dizisinin büyük harfli olması nedeniyle tam olarak C# içindeki bir dizgede ToUpper'ı çağırmaktan farklı bir bayt dizisi üretecektir. tek tek (bu yüzden ikinci 2 bayt dizisi BMP 0-0xFFFF aralığında ve potansiyel olarak genişletilmiş olacaktır), CLR String.ToUpper muhtemelen çiftini dikkate almak ve büyük harf temsil eden yeni bir çift üretmek . – Triynko

+0

Muhtemelen "Hangi dizi dönüşümleri nötr nötr olan?" Gibi tamamen farklı bir soru sorabilirdim. Durumun değiştirilmesi, karakter uzunluğunun bulunması, ipin karşılaştırılması/ayrıştırılması, tersine çevirilmesi vb. Büyük olasılıkla nötr değil, ama ne olacak? Sanırım belki de hiçbiri yoktur, bu yüzden ifadenize katılıyorum: “bu değer dönüşümlerini karakter değerlerini dikkate almadan yapmak her zaman tehlikeli bir faaliyettir”. – Triynko

+0

@Triynko - Vekil kod noktaları, UCS-2'de saydam olacak şekilde özel olarak tahsis edilmiştir. Önde gelen bir vekil veya takip eden bir vekil büyük harfle girmeye çalışmak her zaman orijinal karaktere eşlenecektir, çünkü bu kod noktaları için tanımlanmış bir durum dönüşümü yoktur. Yüksek düzlemlerde (şüpheliyim) tanımlanan büyük/küçük harf dönüşümleri olduğunu varsayarsak, CLR ve TSQL farklı bir dönüşüm gerçekleştirir, ancak hiçbir işlem önemsiz veri üretmez (TSQL bu karakterleri değiştirmez). ... –

İlgili konular