2011-04-13 5 views
8

nchar/nvarchar 필드에 대해 SQL Server가 유니 코드 UCS-2 (2 바이트 고정 길이 문자 인코딩)을 사용하는 것으로 보입니다. 한편 C#은 문자열에 대해 유니 코드 UTF-16 인코딩을 사용합니다 (참고 : UCS-2를 유니 코드로 간주하지 않는 사람이 있지만 유니 코드 하위 집합 0-0xFFFF에서 UTF-16과 동일한 코드 포인트를 모두 인코딩합니다. 그리고 SQL Server에 관한 한 그것이 문자열의면에서 기본적으로 지원하는 "Unicode"에 가장 가까운 것입니다.SQL Server nvarchar (UCS-2) 열에 C# 문자열 (UTF-16)을 저장하면 어떤 결과가 발생합니까?

UCS-2는 기본 다국 언어 평면에서 UTF-16과 동일한 기본 코드 포인트를 인코딩하지만 (BMP)를 사용하면 UTF-16이 서로 게이트 쌍을 허용하는 특정 비트 패턴을 예약하지 않습니다.

SQL Server nvarchar (UCS-2) 필드에 C# 문자열을 쓰고 다시 읽으면이 결과가 항상 반환됩니까?

UTF-16은 UTF-16이 더 많은 코드 포인트 (예 : 0xFFFF)를 인코딩한다는 점에서 UCS-2의 상위 세트 인 것처럼 보이지만 실제로는 2 바이트에서 UCS-2의 서브 세트입니다 레벨이 더 제한적이기 때문에

내 자신의 질문에 대답하기 위해 C# 문자열에 0xFFFF (문자 쌍으로 표시) 이상의 코드 포인트가 포함되어 있으면 이러한 문자열이 데이터베이스에 저장되고 검색되지만, (예를 들어 TOUPPER를 호출하거나 다른 모든 문자를 공백으로 채우는 등) SQL Server에 대리 쌍을 인식하고 UTF-16으로 nchar/nvarchar 문자열을 효과적으로 처리하지 않으면 문자열을 표시하는 데 몇 가지 문제가 발생할 수 있습니다. .

답변

3

정말 멋진 퍼지입니다.

먼저 유사성

  • 는 SQL 2 바이트 문자의 문자열로 서버 nchar/nvarchar/ntext 데이터 형식 저장 텍스트입니다. 검색 및 정렬을 수행 할 때까지는 사용자가 입력 한 내용을 실제로 신경 쓰지 않습니다 (적절한 Unicode 데이터 정렬 순서를 사용합니다).
  • 데이터 형식은 또한 텍스트를 2 바이트 Char 문자열로 저장합니다. 또한 검색 및 분류 작업을 수행 할 때까지 사용자가 입력 한 내용을 실제로 신경 쓰지 않습니다 (적절한 문화권 방법을 사용함). 이제

차이

  • .NET 당신이 StringInfo 클래스를 통해 CLR 문자열에 실제 유니 코드 코드 포인트에 액세스 할 수 있습니다.
  • .NET에는 다양한 인코딩으로 텍스트 데이터를 인코딩하고 디코딩하는 데 수 많은 지원이 있습니다. 임의의 바이트 스트림을 String으로 변환 할 때는 항상 문자열을 UTF-16으로 인코딩합니다 (완전한 다국어 지원이 지원됨).

은 즉, 은 당신이 텍스트의 전체 모양 등 모두 CLR 및 SQL 서버 문자열 변수 치료로, 당신은 자유롭게 정보의 손실없이 다른 하나에서 할당 할 수 있습니다. 기본 저장소 형식은 완전히 동일하지만 상단에 계층화 된 추상화가 약간 다릅니다.

+0

좋아, 그래서 읽기/쓰기 문자열로 대리자 쌍으로 해석 될 내용이 포함되어 있더라도 nvarchar 필드에 대한 전체 엔터티는 문제 또는 정보 손실을 발생시키지 않습니다. 자, C# 문자열을 char 컬럼에 작성하는 것은 어떻습니까? 나는 약간의 해석과 변환을 필요로하고 데이터 손실을 야기 할 것이라고 생각한다 ... – Triynko

+0

싱글 바이트 컬럼은 검색과 정렬 규칙을 정의 할뿐만 아니라, 문자가 허용됩니다. 열의 Z 드 페이지에있는 값에 맵핑 된 유니 Z 드 Z 드점은 보존되고 나머지는 v 려집니다. –

+0

폐기되었거나 특정 더미 또는 "비 문자"바이트로 대체 되었습니까? 1 바이트 코드 페이지가 비 문자에 대해 특정 바이트를 보유합니까? 대상 코드 공간에 정의되지 않은 유니 코드 문자가 물음표로 바뀌 었음을 보여주는 몇 가지 예를 보았지만 실제로는 문자가 표시되지 않는 방법을 보았을 것입니다. – Triynko

4

텍스트를 UCS-2로 취급하면 많은 문제가 발생할 것으로 예상되지 않습니다.

(AFAIK) BMP 위에 대소 문자 매핑이 없기 때문에 대/소문자 문자가 자체적으로 매핑되기 때문에 대/소문자 변환이 문제가되어서는 안됩니다.

다른 모든 문자를 공백으로 두는 것은 문제를 묻는 것입니다. 실제로 캐릭터의 가치를 고려하지 않고 이러한 종류의 변형을 수행하는 것은 항상 위험한 활동입니다. 문자열 잘림으로 합법적으로 발생하는 것을 볼 수 있습니다. 그러나 타의 추종을 불허하는 대리모가 결과에 나타나면이 자체는 거대한 문제가 아닙니다. 그러한 데이터를 수신하는 모든 시스템은 신경 쓰지 않는 대리모를 교체 문자로 교체 할 수 있습니다.

분명히 문자열 길이는 문자 수가 아닌 바이트/2가 될 것이지만, 유니 코드 코드 차트의 깊이를 배관하면 문자 수는별로 유용하지 않습니다. 예를 들어 문자, RTL 언어, 방향 제어 문자, 태그 및 여러 종류의 공백 문자 조합으로 인해 ASCII 범위를 벗어나면 고정 폭 디스플레이에서 좋은 결과를 얻지 못할 것입니다. 높은 코드 포인트는 귀하의 문제 중 가장 적은 것입니다.

단지 안전한 측면에 있기 위해, 설형 문자 텍스트를 고고학자의 이름과 다른 열에 저장해야합니다. : 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 

경우 누군가가 저렛 글꼴이 설치되어 그냥에서, 여기 당신의 즐거움을위한 실제 문자열입니다

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

응답 해 주셔서 감사합니다. 나는 케이스 변환이 문제가되지 않을 것이라고 동의한다. 예를 들어, 데이터베이스의 문자열에 대해 TOUPPER를 호출하면 C#의 문자열에서 ToUpper를 호출하는 것과 다른 바이트 시퀀스가 ​​생성됩니다. 단, 서로 게이트 쌍이 있으면 TSQL TOUPPER는 대문자로 된 두 바이트 시퀀스를 두 번째 2 바이트 시퀀스는 BMP 0-0xFFFF 범위에 있고 잠재적으로 대문자로 표시됩니다. 반면 CLR String.ToUpper는 대용 쌍을 고려하여 대문자를 나타내는 새 쌍을 생성합니다 . – Triynko

+0

"대체로 중립적 인 문자열 변환은 무엇입니까?"와 같은 완전히 다른 질문을 할 수 있습니다. 대소 문자 바꾸기, 문자 길이 찾기, 문자열 비교/정렬, 반전 등은 대체로 중립을 대신 할 수 없지만 트리밍은 어떨까요? 아마 아무 것도 없다고 생각합니다. 그 이유는 "성격의 가치를 고려하지 않고 이러한 종류의 변형을하는 것은 항상 위험한 활동입니다"라는 귀하의 성명에 동의하는 이유입니다. – Triynko

+0

@Triynko - 서로 게이트 코드 포인트는 UCS-2에서 투명 해 지도록 특별히 할당됩니다. 선행 대리 또는 후행 대리 문자를 대문자로 만들려고하면 해당 코드 포인트에 대해 대/소문자 변환이 정의되지 않았으므로 항상 원래 문자로 다시 매핑됩니다. 상위 평면에 대문자 변환이 정의되어 있다고 가정하면 CLR과 TSQL이 변환을 다르게 수행하지만 어느 작업도 정크 데이터를 생성하지 않습니다 (TSQL은 해당 문자를 변경하지 않으므로). ... –