2011-04-13 19 views
8

ऐसा लगता है कि SQL सर्वर यूनिकोड UCS-2, nchar/nvarchar फ़ील्ड के लिए 2-बाइट निश्चित-लंबाई वर्ण एन्कोडिंग का उपयोग करता है। इस बीच, सी # यूनिकोड यूटीएफ -16 अपने तारों के लिए एन्कोडिंग का उपयोग करता है (नोट: कुछ लोग यूसीएस -2 को यूनिकोड मानते नहीं हैं, लेकिन यह यूनिकोड सबसेट 0-0xFFFF में यूटीएफ -16 के समान कोड कोड को एन्कोड करता है, और जहां तक ​​SQL सर्वर का संबंध है, यह "यूनिकोड" की सबसे नज़दीकी चीज है जो इसे मूल रूप से वर्ण तारों के संदर्भ में समर्थन देती है।)SQL सर्वर nvarchar (UCS-2) कॉलम में C# स्ट्रिंग (UTF-16) को संग्रहीत करने के क्या परिणाम हैं?

जबकि यूसीएस -2 मूल बहुभाषी विमान में यूटीएफ -16 के समान मूल कोड बिंदुओं को एन्कोड करता है (बीएमपी), यह कुछ बिट पैटर्न आरक्षित नहीं करता है कि यूटीएफ -16 सरोगेट जोड़े के लिए अनुमति देता है।

यदि मैं एक SQL सर्वर nvarchar (यूसीएस -2) फ़ील्ड में एक सी # स्ट्रिंग लिखता हूं और इसे वापस पढ़ता हूं, तो क्या यह हमेशा एक ही परिणाम देगा?

ऐसा लगता है कि यूटीएफ -16 यूसीएस -2 के सुपरसैट है, इस अर्थ में कि यूटीएफ -16 अधिक कोड पॉइंट्स (जैसे 0xFFFF से ऊपर) एन्कोड करता है, यह वास्तव में 2-बाइट पर यूसीएस -2 का उप-सेट है स्तर, क्योंकि यह अधिक प्रतिबंधक है।

मेरे अपने प्रश्न का उत्तर देने के लिए, मुझे संदेह है कि यदि मेरे सी # स्ट्रिंग में 0xFFFF (वर्णों के जोड़े द्वारा प्रतिनिधित्व) से ऊपर कोड बिंदु शामिल हैं, तो इन्हें डेटाबेस में ठीक से संग्रहीत और पुनर्प्राप्त किया जाएगा, लेकिन अगर मैंने उन्हें छेड़छाड़ करने की कोशिश की डेटाबेस (उदाहरण के लिए शायद TOUPPER को कॉल करना या हर दूसरे चरित्र को रिक्त करने का प्रयास करना), तो मैं स्ट्रिंग को बाद में प्रदर्शित करने में कुछ समस्याओं में भाग सकता हूं ... जब तक कि SQL सर्वर में ऐसे कार्य नहीं होते हैं जो सरोगेट जोड़ों को स्वीकार करते हैं और nchar/nvarchar स्ट्रिंग्स को यूटीएफ -16 के रूप में प्रभावी ढंग से इलाज करते हैं ।

उत्तर

3

यह वास्तव में एक झगड़ा है।

पहले समानताएं

  • एसक्यूएल सर्वर nchar/nvarchar/ntext 2-बाइट वर्णों की स्ट्रिंग के रूप में डेटा प्रकार की दुकान पाठ। जब तक आप खोज और सॉर्टिंग नहीं करते हैं तब तक यह वास्तव में परवाह नहीं करता है (तब यह उपयुक्त यूनिकोड संयोजन अनुक्रम का उपयोग करता है)।
  • सीएलआर String डेटा प्रकार टेक्स्ट को 2-बाइट Char एस की स्ट्रिंग के रूप में भी संग्रहीत करता है। जब तक आप खोज और सॉर्टिंग नहीं करते हैं तब तक यह वास्तव में परवाह नहीं करता है (तब यह उपयुक्त संस्कृति-विशिष्ट तरीकों का उपयोग करता है)।

अब मतभेद

  • नेट आप StringInfo वर्ग के माध्यम से एक CLR स्ट्रिंग में वास्तविक यूनिकोड कोड अंक तक पहुँचने के लिए अनुमति देता है।
  • .NET के विभिन्न एन्कोडिंग में एन्कोडिंग और डीकोडिंग टेक्स्ट डेटा के लिए बहुत अधिक समर्थन है। एक मनमानी बाइट स्ट्रीम को String में परिवर्तित करते समय, यह हमेशा स्ट्रिंग को यूटीएफ -16 (पूर्ण बहुभाषी विमान समर्थन के साथ) के रूप में एन्कोड करेगा।

संक्षेप में, जब तक आप पाठ के पूरे धब्बे के रूप में दोनों CLR और SQL सर्वर स्ट्रिंग चर का इलाज है, तो आप स्वतंत्र रूप से एक से दूसरे के लिए जानकारी का कोई नुकसान के साथ प्रदान कर सकते हैं। अंतर्निहित भंडारण प्रारूप बिल्कुल वही है, भले ही शीर्ष पर स्तरित अबास्ट्रक्शन थोड़ा अलग हैं।

+0

ठीक है, तो पढ़ने/स्ट्रिंग के रूप में लिख एक nvarchar क्षेत्र के लिए एक पूरी इकाई समस्या या नुकसान की जानकारी का कारण नहीं होगा, भले ही इसमें सरोगेट जोड़े के रूप में क्या व्याख्या की जाएगी। अब, एक सी कॉलम को सी # स्ट्रिंग लिखने के बारे में क्या? मुझे संदेह है कि कुछ व्याख्या और रूपांतरण शामिल होगा और डेटा हानि का कारण बन जाएगा ... – Triynko

+0

एकल-बाइट कॉलम में उन पर परिभाषित एक गैर-यूनिकोड संयोजन अनुक्रम है, जो न केवल नियमों को खोजना और क्रमबद्ध करना परिभाषित करता है, बल्कि कोड पृष्ठ जो परिभाषित करता है पात्रों की अनुमति है। कॉलम के कोड पेज में किसी मान पर मैप किए गए किसी यूनिकोड कोड पॉइंट को संरक्षित किया जाएगा, और शेष को त्याग दिया जाएगा। –

+0

त्याग दिया ... या एक विशेष डमी या "गैर चरित्र" बाइट के साथ प्रतिस्थापित किया? एकल-बाइट कोड पेज गैर-वर्णों के लिए एक निश्चित बाइट आरक्षित करते हैं? मैंने कुछ उदाहरण दिखाए हैं कि लक्ष्य कोड स्थान में परिभाषित यूनिकोड वर्णों को प्रश्न चिह्न के साथ प्रतिस्थापित किया गया है, लेकिन शायद यह कि कैसे गैर-वर्ण प्रदर्शित होते हैं? – Triynko

4

मुझे उम्मीद नहीं है कि टेक्स्ट को यूसीएस -2 के रूप में देखते हुए कई समस्याएं पैदा होंगी।

केस रूपांतरण कोई समस्या नहीं होनी चाहिए, क्योंकि (AFAIK) बीएमपी के ऊपर कोई मामला मैपिंग नहीं है (पहचान मैपिंग को छोड़कर, निश्चित रूप से!), और जाहिर है, सरोगेट वर्ण खुद को मानचित्रण करने जा रहे हैं।

हर दूसरे चरित्र को झुकाव सिर्फ परेशानी के लिए पूछ रहा है। हकीकत में, चरित्र मूल्यों के विचार के बिना इन प्रकार के परिवर्तनों को करना हमेशा एक खतरनाक गतिविधि है। मैं इसे स्ट्रिंग ट्रंकेशन के साथ वैध रूप से हो रहा देख सकता हूं। लेकिन यदि परिणाम में कोई बेजोड़ सरोगेट दिखाई देता है, तो यह विशाल समस्या नहीं है। कोई भी सिस्टम जो इस तरह के डेटा प्राप्त करता है और परवाह करता है-शायद बेमिसाल सरोगेट को एक प्रतिस्थापन चरित्र के साथ प्रतिस्थापित करेगा, अगर यह इसके बारे में कुछ भी करने के लिए परेशान है।

जाहिर है, स्ट्रिंग लम्बाई संख्याओं की बजाय बाइट्स/2 होने जा रही है, लेकिन यूनिकोड कोड चार्ट की गहराई को प्लंबिंग शुरू करने के बाद संख्या-वर्ण-वर्ण एक बहुत उपयोगी मूल्य नहीं है। उदाहरण के लिए, वर्णों, आरटीएल भाषाओं, दिशात्मक नियंत्रण वर्णों, टैग, और कई प्रकार के अंतरिक्ष वर्णों को संयोजित करने के कारण, आप ASCII रेंज छोड़ने के बाद मोनोस्पेस्ड डिस्प्ले में अच्छे नतीजे नहीं प्राप्त करेंगे। उच्च कोड बिंदु आपकी समस्याओं का कम से कम होने जा रहे हैं।

बस सुरक्षित पक्ष पर रहने के लिए, आपको संभवतः पुरातत्वविदों के नामों की तुलना में अपने क्यूनिफॉर्म ग्रंथों को एक अलग कॉलम में स्टोर करना चाहिए। : डी

अब अनुभवजन्य डेटा के साथ अद्यतन करें!

मैंने अभी यह देखने के लिए एक परीक्षण चलाया कि केस ट्रांसफॉर्मेशन के साथ क्या होता है। मैंने लैटिन स्क्रिप्ट में दो बार अपरकेस में अंग्रेजी शब्द टेस्ट के साथ एक स्ट्रिंग बनाई, फिर डीज़्रेट स्क्रिप्ट में। मैंने .NET और SQL सर्वर में इस स्ट्रिंग में निम्न-केस परिवर्तन लागू किया।

.NET संस्करण दोनों स्क्रिप्ट में सभी अक्षरों को सही ढंग से कम कर देता है। SQL सर्वर संस्करण ने केवल लैटिन वर्णों को कम किया और डीज़्रेट वर्णों को अपरिवर्तित छोड़ दिया। यह यूटीएफ -16 छंद यूसीएस -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 

मामले किसी को भी एक डीज़रेट फ़ॉन्ट स्थापित किया है बस में, यहाँ अपने आनंद के लिए वास्तविक तार कर रहे हैं:

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

प्रतिक्रिया के लिए धन्यवाद। मैं असहमत हूं कि केस रूपांतरण कोई समस्या नहीं होगी। उदाहरण के लिए, डेटाबेस में एक स्ट्रिंग पर TOUPPER को कॉल करने से C# में स्ट्रिंग पर ToUpper को कॉल करने से अलग बाइट-अनुक्रम उत्पन्न होगा, ठीक है क्योंकि यदि सरोगेट जोड़ी मौजूद है, तो TSQL TOUPPER ऊपरी-केस प्रत्येक 2-बाइट अनुक्रम का होगा व्यक्तिगत रूप से जोड़ी (इसलिए दूसरा 2-बाइट अनुक्रम बीएमपी 0-0xFFFF रेंज में गिर सकता है और संभावित रूप से ऊपरी हो सकता है), जबकि सीएलआर स्ट्रिंग। टॉपर शायद सरोगेट जोड़ी को ध्यान में रखेगा और ऊपरी मामले के पत्र का प्रतिनिधित्व करने वाली एक नई जोड़ी का उत्पादन करेगा । – Triynko

+0

मैं शायद एक पूरी तरह से अलग सवाल पूछ सकता हूं जैसे "क्या स्ट्रिंग ट्रांसफॉर्म सरोगेट तटस्थ हैं?"। केस बदलना, चरित्र की लंबाई ढूंढना, स्ट्रिंग की तुलना करना/सॉर्ट करना, इसे उलटना, इत्यादि शायद सरोगेट तटस्थ नहीं होगा, लेकिन ट्रिमिंग के बारे में क्या होगा? मुझे लगता है कि शायद कोई नहीं है, यही कारण है कि मैं आपके बयान से सहमत हूं कि "चरित्र मूल्यों के विचार के बिना इन प्रकार के परिवर्तनों को हमेशा एक खतरनाक गतिविधि है"। – Triynko

+0

@Triynko - सरोगेट कोड बिंदु विशेष रूप से आवंटित किए जाते हैं ताकि वे यूसीएस -2 में पारदर्शी हों। किसी भी प्रमुख सरोगेट या पिछली सरोगेट को अपरकेस करने का प्रयास हमेशा मूल चरित्र पर वापस आ जाएगा, क्योंकि उन कोड बिंदुओं के लिए कोई केस रूपांतरण परिभाषित नहीं किया गया है। अगर हम मानते हैं कि उच्च विमानों (जो मुझे संदेह है) में परिभाषित केस रूपांतरण हैं, तो सीएलआर और टीएसक्यूएल रूपांतरण को अलग-अलग प्रदर्शन करेंगे, लेकिन न तो ऑपरेशन जंक डेटा उत्पन्न करेगा (क्योंकि टीएसक्यूएल उन पात्रों को अपरिवर्तित छोड़ देगा)। ... –