다음을 통해 공유


.NET Framework의 문자 인코딩

업데이트: 2010년 10월

문자는 여러 가지 방법으로 나타낼 수 있는 추상 엔터티입니다. 문자 인코딩은 지원되는 문자 집합의 각 문자를 해당 문자를 나타내는 값과 연결하는 시스템입니다. 예를 들어 모르스 부호는 로마 알파벳 문자를 전신 회선으로 전송하기에 적합한 점과 대시의 패턴과 연결한 문자 인코딩입니다. 컴퓨터에서 문자 인코딩은 지원되는 문자 집합의 각 문자를 해당 문자를 나타내는 숫자 값과 연결합니다. 문자 인코딩에는 두 가지 구분되는 구성 요소가 포함됩니다.

  • 인코더 - 문자 시퀀스를 숫자 값(바이트) 시퀀스로 변환합니다.

  • 디코더 - 바이트 시퀀스를 문자 시퀀스로 변환합니다.

문자 인코딩은 인코더와 디코더가 작동하는 규칙을 설명합니다. 예를 들어 UTF8Encoding 클래스는 단일 유니코드 문자를 나타내기 위해 1~4바이트를 사용하는 8비트 유니코드 변환 형식(UTF-8)에 대한 인코딩 및 디코딩 규칙을 설명합니다. 인코딩 및 디코딩에는 유효성 검사도 포함될 수 있습니다. 예를 들어 UnicodeEncoding 클래스는 모든 서로게이트가 올바른 서로게이트 쌍으로 구성되었는지 확인합니다. 서로게이트 쌍은 U+D800에서 U+DBFF까지의 코드 포인트를 포함하는 문자와 U+DC00에서 U+DFFF까지의 코드 포인트를 포함하는 문자로 구성됩니다. fallback 전략은 인코더가 잘못된 문자를 처리하거나 디코더가 잘못된 바이트를 처리하는 방법을 결정합니다.

주의 정보주의

.NET Framework 인코딩 클래스는 문자 데이터를 저장 및 변환하기 위한 방법을 제공합니다.이러한 인코딩 클래스는 이진 데이터를 문자열 형식으로 저장하는 데 사용할 수 없습니다.사용된 인코딩에 따라 인코딩 클래스를 사용하여 이진 데이터를 문자열 형식으로 변환하면 예기치 않은 동작이 발생하고 데이터가 정확하지 않게 되거나 손상될 수 있습니다.이진 데이터를 문자열 형식으로 변환하려면 Convert.ToBase64String(Byte[]) 메서드를 사용합니다.

공용 언어 런타임을 대상으로 하는 응용 프로그램은 인코더를 사용하여 공용 언어 런타임으로 지원되는 유니코드 문자 표시를 다른 인코딩 체계로 매핑합니다. 이러한 응용 프로그램은 디코더를 사용하여 유니코드가 아닌 인코딩에서 유니코드로 문자를 매핑합니다.

이 항목은 다음과 같은 단원으로 구성됩니다.

.NET Framework의 인코딩

.NET Framework의 모든 문자 인코딩 클래스는 모든 문자 인코딩에 대한 공통 기능을 정의하는 추상 클래스인 System.Text.Encoding 클래스에서 상속됩니다. .NET Framework에서 구현되는 개별 인코딩 개체에 액세스하려면 다음을 수행합니다.

  • .NET Framework에서 제공되는 표준 문자 인코딩(ASCII, UTF-7, UTF-8, UTF-16 및 UTF-32)을 나타내는 개체를 반환하는 Encoding 클래스의 정적 속성을 사용합니다. 예를 들어 Encoding.Unicode 속성은 UnicodeEncoding 개체를 반환합니다. 각 개체는 인코딩할 수 없는 문자열과 디코딩할 수 없는 바이트를 처리하기 위해 대체 fallback을 사용합니다. 자세한 내용은 대체 fallback 단원을 참조하십시오.

  • 인코딩의 클래스 생성자를 호출합니다. ASCII, UTF-7, UTF-8, UTF-16 및 UTF-32 인코딩에 대한 개체는 이 방식으로 인스턴스화할 수 있습니다. 기본적으로 각 개체는 인코딩할 수 없는 문자열과 디코딩할 수 없는 바이트를 처리하기 위해 대체 fallback을 사용하지만 대신 예외가 발생되도록 지정할 수 있습니다. 자세한 내용은 대체 fallback예외 fallback 단원을 참조하십시오.

  • Encoding.Encoding(Int32) 생성자를 호출하고 여기에 인코딩을 나타내는 정수를 전달합니다. 대체 fallback을 사용하는 표준 인코딩 개체와 코드 페이지 및 DBCS(더블바이트 문자 집합) 인코딩 개체는 인코딩할 수 없는 문자열 및 디코딩할 수 없는 바이트를 처리하기 위해 가장 적합한 fallback을 사용합니다. 자세한 내용은 최적 문자 fallback 단원을 참조하십시오.

  • .NET Framework에서 사용할 수 있는 모든 표준, 코드 페이지 또는 DBCS 인코딩을 반환하는 Encoding.GetEncoding 메서드를 호출합니다. 오버로드는 인코더 및 디코더에 대해 fallback 개체를 지정할 수 있게 해줍니다.

참고참고

유니코드 표준에서는 지원되는 모든 스크립트의 각 문자에 코드 포인트(숫자)와 이름을 할당합니다.예를 들어, 문자 "A"는 코드 포인트 U+0041과 이름 "LATIN CAPITAL LETTER A"로 표시됩니다.UTF(유니코드 변환 형식) 인코딩은 이러한 코드 포인트를 하나 이상의 바이트 시퀀스로 인코딩하기 위한 방법을 정의합니다.유니코드 인코딩 체계는 모든 문자 집합의 문자를 하나의 인코딩으로 표시할 수 있으므로 지역화 대비 응용 프로그램 개발을 간단하게 만듭니다.응용 프로그램 개발자는 특정 언어 또는 문자 환경의 문자를 만들기 위해 사용하던 인코딩 체계를 더 이상 추적할 필요가 없으며 데이터도 손상 없이 국제적으로 시스템 간에 공유할 수 있습니다.

.NET Framework에서는 유니코드 표준으로 정의된 세 가지 인코딩 방식인 UTF-8, UTF-16 및 UTF-32가 지원됩니다.자세한 내용은 Unicode 홈 페이지에서 유니코드 표준을 참조하십시오.

Encoding.GetEncodings 메서드를 호출하면 .NET Framework에서 제공되는 모든 인코딩에 대한 정보를 검색할 수 있습니다. .NET Framework에서는 다음 표에 나열된 문자 인코딩 체계가 지원됩니다.

인코딩

클래스

설명

장점/단점

ASCII

[ T:System.Text.ASCIIEncoding ]

1바이트 중에서 하위 7비트를 사용하여 제한된 범위의 문자를 인코딩합니다.

이 인코딩은 U+0000에서 U+007F 사이의 문자 값만 지원하므로 대부분의 경우 국제화된 응용 프로그램에는 적합하지 않습니다.

UTF-7

[ T:System.Text.UTF7Encoding ]

문자를 7비트 ASCII 문자 시퀀스로 나타냅니다. ASCII가 아닌 유니코드 문자는 ASCII 문자의 이스케이프 시퀀스로 나타냅니다.

UTF-7은 전자 메일 및 뉴스 그룹 등의 프로토콜을 지원합니다. 그러나 UTF-7은 특별히 안전하거나 강력하지는 않습니다. 1비트만 변경해도 전체 UTF-7 문자열의 해석이 크게 바뀌는 경우가 있을 수 있으며, 서로 다른 UTF-7 문자열이 동일한 텍스트로 인코딩될 수도 있습니다. 시퀀스에 ASCII가 아닌 문자가 포함된 경우에는 UTF-7이 UTF-8보다 필요한 공간이 더 많으며 인코딩/디코딩 속도가 느립니다. 따라서 가능하면 UTF-7 대신 UTF-8을 사용해야 합니다.

UTF-8

[ T:System.Text.UTF8Encoding ]

각 유니코드 코드 포인트를 1~4바이트의 시퀀스로 나타냅니다.

UTF-8은 8비트 데이터 크기를 지원하며 많은 기존 운영 체제에서 정상적으로 작동합니다. ASCII 문자 범위에서는 UTF-8이 ASCII 인코딩과 동일하며 보다 큰 문자 집합을 사용할 수 있습니다. 하지만 CJK(중국어-일본어-한국어)의 경우 UTF-8에서는 각 문자에 3바이트가 필요하므로 UTF-16의 경우보다 데이터가 커질 수 있습니다. HTML 태그와 같은 ASCII 데이터의 양이 많으면 CJK 범위에서 크기가 다소 증가되더라도 전체 크기가 감소할 수도 있습니다.

UTF-16

[ T:System.Text.UnicodeEncoding ]

각 유니코드 코드 포인트를 1~2개의 16비트 정수 시퀀스로 나타냅니다. 유니코드 보완 문자(U+10000 이상)에는 두 개의 UTF-16 서로게이트 코드 포인트가 필요하지만 대부분의 일반적인 유니코드 문자에는 UTF-16 코드 포인트가 하나만 필요합니다. little-endian 및 big-endian 바이트 순서가 모두 지원됩니다.

UTF-16 인코딩은 공통 언어 런타임에서 CharString 값을 나타내기 위해 사용되며, Windows 운영 체제에서는 WCHAR 값을 나타내기 위해 사용됩니다.

UTF-32

[ T:System.Text.UTF32Encoding ]

각 유니코드 코드 포인트를 32비트 정수로 나타냅니다. little-endian 및 big-endian 바이트 순서가 모두 지원됩니다.

UTF-32 인코딩은 응용 프로그램에서 인코딩된 공간이 중요하게 사용되는 운영 체제에서 나타나는 UTF-16 인코딩의 서로게이트 코드 포인트 동작을 막으려는 경우에 사용됩니다. 문자 모양 하나가 화면에 표시되어도 실제로는 둘 이상의 UTF-32 문자로 인코딩되었을 수 있습니다.

ANSI/ISO 인코딩

다양한 코드 페이지에 대한 지원을 제공합니다. Windows 운영 체제에서는 특정 언어 또는 언어 그룹을 지원하기 위해 코드 페이지가 사용됩니다. .NET Framework에서 지원되는 코드 페이지가 나열된 표를 보려면 Encoding 클래스를 참조하십시오. Encoding.GetEncoding(Int32) 메서드를 호출하여 특정 코드 페이지에 대한 인코딩 개체를 검색할 수 있습니다.

코드 페이지에는 256개의 코드 포인트가 포함되며 0부터 시작합니다. 대부분의 코드 페이지에서 코드 포인트 0에서 127는 ASCII 문자 집합을 나타내며 128에서 255까지의 코드 포인트는 코드 페이지마다 크게 다릅니다. 예를 들어 코드 페이지 1252는 영어, 독일어 및 프랑스어를 포함하는 라틴어 문자 환경에 대한 문자를 제공합니다. 코드 페이지 1252에서 마지막 128개의 코드 포인트에는 악센트 문자가 포함됩니다. 코드 페이지 1253에서는 그리스어 문자 환경에 필요한 문자 코드를 제공하며 코드 페이지 1253에서 마지막 128개의 코드 포인트에는 그리스어 문자가 포함됩니다. 따라서 ANSI 코드 페이지를 사용하는 응용 프로그램은 참조되는 코드 페이지를 나타내는 식별자를 포함하지 않는 한 그리스어와 독일어를 동일한 텍스트 스트림으로 저장할 수 없습니다.

BDCS(더블바이트 문자 집합) 인코딩

중국어, 일본어 및 한국어와 같이 256개 이상의 문자가 포함된 언어를 지원합니다. DBCS에서는 한 쌍의 코드 포인트(더블바이트)가 각 문자를 나타냅니다. Encoding.IsSingleByte 속성은 DBCS 인코딩에 대해 false를 반환합니다. Encoding.GetEncoding(Int32) 메서드를 호출하여 특정 DBCS에 대한 인코딩 개체를 검색할 수 있습니다.

DBCS에서는 한 쌍의 코드 포인트(더블바이트)가 각 문자를 나타냅니다. 응용 프로그램에서 DBCS 데이터를 처리할 때 DBCS 문자의 첫 번째 바이트(선행 바이트)는 바로 뒤에 오는 후행 바이트와 조합하여 처리됩니다. 더블바이트 코드 포인트의 단일 쌍은 코드 페이지에 따라 서로 다른 문자를 나타낼 수 있으므로 이 체계에서는 일본어 및 중국어와 같은 두 개의 언어를 동일한 데이터 스트림으로 조합할 수 없습니다.

이러한 인코딩에서는 유니코드 문자뿐만 아니라 레거시 응용 프로그램에서 가장 공통적으로 사용되는 인코딩도 사용할 수 있습니다. 또한 Encoding에서 파생되는 클래스를 정의하고 해당 멤버를 재정의하여 사용자 지정 인코딩을 만들 수 있습니다.

인코딩 클래스 선택

응용 프로그램에서 사용할 인코딩을 선택할 수 있는 경우에는 UTF8Encoding 또는 UnicodeEncoding과 같은 유니코드 인코딩을 사용하는 것이 좋습니다. .NET Framework에서는 또한 세 번째 유니코드 인코딩인 UTF32Encoding이 지원됩니다.

ASCII 인코딩(ASCIIEncoding)을 사용하려는 경우에는 대신 UTF8Encoding을 선택하십시오. 두 인코딩 모두 ASCII 문자 집합이 동일하지만 UTF8Encoding의 경우 다음과 같은 장점이 있습니다.

  • ASCIIEncoding은 U+0000에서 U+007F 사이의 유니코드 문자 값만 지원하지만 UTF8Encoding은 모든 유니코드 문자를 나타낼 수 있습니다.

  • 오류 검색 기능과 더 나은 보안 기능을 제공합니다.

  • 인코딩 속도를 가능한 한 높일 수 있도록 조정되었으므로 다른 인코딩보다 속도가 빠릅니다. 콘텐츠 전체가 ASCII인 경우에도 UTF8Encoding으로 작업을 수행하는 속도가 ASCIIEncoding의 경우보다 빠릅니다.

ASCIIEncoding은 레거시 응용 프로그램에만 사용하는 것이 좋습니다. 하지만 레거시 응용 프로그램의 경우에도 기본 설정을 사용한다고 가정했을 때 다음과 같은 경우에는 UTF8Encoding이 더 나은 선택일 수 있습니다.

  • 응용 프로그램에서 일부가 ASCII가 아닌 콘텐츠를 ASCIIEncoding으로 인코딩하면 ASCII가 아닌 각 문자가 물음표(?)로 인코딩됩니다. 그런 다음 응용 프로그램에서 이 데이터를 인코딩하면 정보가 손실됩니다.

  • 응용 프로그램에서 일부가 ASCII가 아닌 콘텐츠를 UTF8Encoding으로 인코딩하면 ASCII로 해석할 때 알아볼 수 없는 결과가 나타납니다. 그러나 응용 프로그램에서 UTF-8 디코더를 사용하여 이 데이터를 디코딩하면 데이터가 성공적으로 복원됩니다.

웹 응용 프로그램에서 웹 요청에 대한 응답으로 클라이언트에 전송된 문자는 클라이언트에서 사용되는 인코딩이 반영되어야 합니다. 대부분의 경우 사용자에게 필요한 인코딩으로 텍스트를 표시하기 위해서는 HttpResponse.ContentEncoding 속성을 HttpRequest.ContentEncoding 속성으로 반환된 값으로 설정해야 합니다.

인코딩 개체 사용

인코더는 문자열(대부분의 경우 유니코드 문자)을 해당 문자의 숫자(바이트) 값으로 변환합니다. 예를 들어 유니코드 문자를 콘솔에 표시할 수 있도록 ASCII 인코더를 사용하여 유니코드 문자를 ASCII로 변환할 수 있습니다. 이러한 변환을 수행하려면 Encoding.GetBytes 메서드를 호출합니다. 인코딩을 수행하기 전에 인코딩된 문자를 저장하기 위해 필요한 바이트 수를 확인하려는 경우 GetByteCount 메서드를 호출하면 됩니다.

다음 예제에서는 단일 바이트 배열을 사용하여 문자열을 두 개의 개별 작업으로 인코딩합니다. 여기에서는 ASCII로 인코딩된 바이트의 다음 집합에 대한 바이트 배열에서 시작 위치를 나타내는 인덱스가 유지 관리됩니다. 이 예에서는 ASCIIEncoding.GetByteCount(String) 메서드를 호출하여 바이트 배열이 인코딩된 문자열을 수용하기에 충분한지 확인합니다. 그런 다음 문자열의 문자를 인코딩하기 위해 ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) 메서드를 호출합니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim strings() As String = { "This is the first sentence. ", 
                                  "This is the second sentence. " }
      Dim asciiEncoding As Encoding = Encoding.ASCII

      ' Create array of adequate size.
      Dim bytes(50) As Byte
      ' Create index for current position of array.
      Dim index As Integer = 0

      Console.WriteLine("Strings to encode:")
      For Each stringValue In strings
         Console.WriteLine("   {0}", stringValue)

         Dim count As Integer = asciiEncoding.GetByteCount(stringValue)
         If count + index >=  bytes.Length Then
            Array.Resize(bytes, bytes.Length + 50)
         End If
         Dim written As Integer = asciiEncoding.GetBytes(stringValue, 0, 
                                                         stringValue.Length, 
                                                         bytes, index)    

         index = index + written 
      Next 
      Console.WriteLine()
      Console.WriteLine("Encoded bytes:")
      Console.WriteLine("{0}", ShowByteValues(bytes, index))
      Console.WriteLine()

      ' Decode Unicode byte array to a string.
      Dim newString As String = asciiEncoding.GetString(bytes, 0, index)
      Console.WriteLine("Decoded: {0}", newString)
   End Sub

   Private Function ShowByteValues(bytes As Byte(), last As Integer) As String
      Dim returnString As String = "   "
      For ctr As Integer = 0 To last - 1
         If ctr Mod 20 = 0 Then returnString += vbCrLf + "   "
         returnString += String.Format("{0:X2} ", bytes(ctr))
      Next
      Return returnString
   End Function
End Module
' The example displays the following output:
'       Strings to encode:
'          This is the first sentence.
'          This is the second sentence.
'       
'       Encoded bytes:
'       
'          54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
'          6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
'          73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
'       
'       Decoded: This is the first sentence. This is the second sentence.
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      string[] strings= { "This is the first sentence. ", 
                          "This is the second sentence. " };
      Encoding asciiEncoding = Encoding.ASCII;

      // Create array of adequate size.
      byte[] bytes = new byte[49];
      // Create index for current position of array.
      int index = 0;

      Console.WriteLine("Strings to encode:");
      foreach (var stringValue in strings) {
         Console.WriteLine("   {0}", stringValue);

         int count = asciiEncoding.GetByteCount(stringValue);
         if (count + index >=  bytes.Length)
            Array.Resize(ref bytes, bytes.Length + 50);

         int written = asciiEncoding.GetBytes(stringValue, 0, 
                                              stringValue.Length, 
                                              bytes, index);    

         index = index + written; 
      } 
      Console.WriteLine("\nEncoded bytes:");
      Console.WriteLine("{0}", ShowByteValues(bytes, index));
      Console.WriteLine();

      // Decode Unicode byte array to a string.
      string newString = asciiEncoding.GetString(bytes, 0, index);
      Console.WriteLine("Decoded: {0}", newString);
   }

   private static string ShowByteValues(byte[] bytes, int last ) 
   {
      string returnString = "   ";
      for (int ctr = 0; ctr <= last - 1; ctr++) {
         if (ctr % 20 == 0)
            returnString += "\n   ";
         returnString += String.Format("{0:X2} ", bytes[ctr]);
      }
      return returnString;
   }
}
// The example displays the following output:
//       Strings to encode:
//          This is the first sentence.
//          This is the second sentence.
//       
//       Encoded bytes:
//       
//          54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
//          6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
//          73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
//       
//       Decoded: This is the first sentence. This is the second sentence.

디코더는 특정 문자 인코딩을 반영하는 바이트 배열을 문자 배열 또는 문자열의 문자 집합으로 변환합니다. 바이트 배열을 문자 배열로 디코딩하려면 Encoding.GetChars 메서드를 호출합니다. 바이트 배열을 문자열로 디코딩하려면 GetString 메서드를 호출합니다. 디코딩을 수행하기 전에 디코딩된 바이트를 저장하기 위해 필요한 문자 수를 확인하려는 경우 GetCharCount 메서드를 호출하면 됩니다.

다음 예제에서는 세 개의 문자열을 인코딩하고 이를 단일 문자 배열로 디코딩합니다. 여기에서는 디코딩된 문자의 다음 집합에 대한 문자 배열에서 시작 위치를 나타내는 인덱스가 유지 관리됩니다. 이 예에서는 GetCharCount 메서드를 호출하여 문자 배열이 디코딩된 모든 문자를 수용하기에 충분한지 확인합니다. 그런 다음 바이트 배열을 디코딩하기 위해 ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) 메서드를 호출합니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim strings() As String = { "This is the first sentence. ", 
                                  "This is the second sentence. ",
                                  "This is the third sentence. " }
      Dim asciiEncoding As Encoding = Encoding.ASCII
      ' Array to hold encoded bytes.
      Dim bytes() As Byte
      ' Array to hold decoded characters.
      Dim chars(50) As Char
      ' Create index for current position of character array.
      Dim index As Integer     

      For Each stringValue In strings
         Console.WriteLine("String to Encode: {0}", stringValue)
         ' Encode the string to a byte array.
         bytes = asciiEncoding.GetBytes(stringValue)
         ' Display the encoded bytes.
         Console.Write("Encoded bytes: ")
         For ctr As Integer = 0 To bytes.Length - 1
            Console.Write(" {0}{1:X2}", If(ctr Mod 20 = 0, vbCrLf, ""), 
                                        bytes(ctr))
         Next         
         Console.WriteLine()

         ' Decode the bytes to a single character array.
         Dim count As Integer = asciiEncoding.GetCharCount(bytes)
         If count + index >=  chars.Length Then
            Array.Resize(chars, chars.Length + 50)
         End If
         Dim written As Integer = asciiEncoding.GetChars(bytes, 0, 
                                                         bytes.Length, 
                                                         chars, index)              
         index = index + written
         Console.WriteLine()       
      Next

      ' Instantiate a single string containing the characters.
      Dim decodedString As New String(chars, 0, index - 1)
      Console.WriteLine("Decoded string: ")
      Console.WriteLine(decodedString)
   End Sub
End Module
' The example displays the following output:
'    String to Encode: This is the first sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
'    6E 74 65 6E 63 65 2E 20
'    
'    String to Encode: This is the second sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
'    65 6E 74 65 6E 63 65 2E 20
'    
'    String to Encode: This is the third sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
'    6E 74 65 6E 63 65 2E 20
'    
'    Decoded string:
'    This is the first sentence. This is the second sentence. This is the third sentence.
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      string[] strings = { "This is the first sentence. ", 
                           "This is the second sentence. ",
                           "This is the third sentence. " };
      Encoding asciiEncoding = Encoding.ASCII;
      // Array to hold encoded bytes.
      byte[] bytes;
      // Array to hold decoded characters.
      char[] chars = new char[50];
      // Create index for current position of character array.
      int index = 0;     

      foreach (var stringValue in strings) {
         Console.WriteLine("String to Encode: {0}", stringValue);
         // Encode the string to a byte array.
         bytes = asciiEncoding.GetBytes(stringValue);
         // Display the encoded bytes.
         Console.Write("Encoded bytes: ");
         for (int ctr = 0; ctr < bytes.Length; ctr++)
            Console.Write(" {0}{1:X2}", 
                          ctr % 20 == 0 ? Environment.NewLine : "", 
                          bytes[ctr]);
         Console.WriteLine();

         // Decode the bytes to a single character array.
         int count = asciiEncoding.GetCharCount(bytes);
         if (count + index >=  chars.Length)
            Array.Resize(ref chars, chars.Length + 50);

         int written = asciiEncoding.GetChars(bytes, 0, 
                                              bytes.Length, 
                                              chars, index);              
         index = index + written;
         Console.WriteLine();       
      }

      // Instantiate a single string containing the characters.
      string decodedString = new string(chars, 0, index - 1);
      Console.WriteLine("Decoded string: ");
      Console.WriteLine(decodedString);
   }
}
// The example displays the following output:
//    String to Encode: This is the first sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
//    6E 74 65 6E 63 65 2E 20
//    
//    String to Encode: This is the second sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
//    65 6E 74 65 6E 63 65 2E 20
//    
//    String to Encode: This is the third sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
//    6E 74 65 6E 63 65 2E 20
//    
//    Decoded string:
//    This is the first sentence. This is the second sentence. This is the third sentence.

Encoding에서 파생된 클래스의 인코딩 및 디코딩 메서드는 전체 데이터 집합에서 작동하도록 디자인되었습니다. 즉, 인코딩 또는 디코딩하려는 모든 데이터가 단일 메서드 호출로 제공됩니다. 하지만 일부 경우에는 데이터가 스트림으로 제공되며, 인코딩 또는 디코딩하려는 데이터는 별도의 읽기 작업으로만 제공될 수 있습니다. 이를 위해서는 인코딩 또는 디코딩 작업 시 이전 호출로 저장된 모든 상태를 기억해야 합니다. EncoderDecoder에서 파생된 클래스의 메서드는 여러 메서드 호출에 걸쳐 있는 인코딩 및 디코딩 작업을 처리할 수 있습니다.

특정 인코딩에 대한 Encoder 개체는 해당 인코딩의 Encoding.GetEncoder 속성으로부터 제공됩니다. 특정 인코딩에 대한 Decoder 개체는 해당 인코딩의 Encoding.GetDecoder 속성으로부터 제공됩니다. 디코딩 작업의 경우 Decoder에서 파생된 클래스에 Decoder.GetChars 메서드가 포함되지만 Encoding.GetString에 해당하는 메서드가 없습니다.

다음 예제에서는 유니코드 바이트 배열을 디코딩하기 위해 사용되는 Encoding.GetCharsDecoder.GetChars 메서드 사이의 차이점을 보여 줍니다. 이 예제에서는 일부 유니코드 문자가 포함된 문자열을 파일로 인코딩한 다음 두 개의 디코딩 메서드를 사용하여 이를 한 번에 10개의 바이트로 디코딩합니다. 서로게이트 쌍은 10번째와 11번째 바이트에 오기 때문에 별도의 메서드 호출로 디코딩됩니다. 결과에서 보여 지듯이 Encoding.GetChars 메서드는 바이트를 올바르게 디코딩할 수 없으며 대신 이를 U+FFFD(REPLACEMENT CHARACTER)로 바꿉니다. 반면에 Decoder.GetChars 메서드는 바이트 배열을 성공적으로 디코딩하여 원래의 문자열을 얻을 수 있습니다.

Imports System.IO
Imports System.Text

Module Example
   Public Sub Main()
      ' Use default replacement fallback for invalid encoding.
      Dim enc As New UnicodeEncoding(True, False, False)

      ' Define a string with various Unicode characters.
      Dim str1 As String = String.Format("AB YZ 19 {0}{1} {2}", 
                                         ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4))
      str1 += String.Format("Unicode characters. {0} {1} s {2}{3}", 
                            ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308))
      Console.WriteLine("Created original string...")
      Console.WriteLine()

      ' Convert string to byte array.                     
      Dim bytes() As Byte = enc.GetBytes(str1)

      Dim fs As FileStream = File.Create(".\characters.bin")
      Dim bw As New BinaryWriter(fs)
      bw.Write(bytes)
      bw.Close()

      ' Read bytes from file.
      Dim fsIn As FileStream = File.OpenRead(".\characters.bin")
      Dim br As New BinaryReader(fsIn)

      Const count As Integer = 10      ' Number of bytes to read at a time. 
      Dim bytesRead(9) As Byte         ' Buffer (byte array).
      Dim read As Integer              ' Number of bytes actually read. 
      Dim str2 As String = ""          ' Decoded string.

      ' Try using Encoding object for all operations.
      Do 
         read = br.Read(bytesRead, 0, count)
         str2 += enc.GetString(bytesRead, 0, read) 
      Loop While read = count
      br.Close()
      Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...")
      CompareForEquality(str1, str2)
      Console.WriteLine()

      ' Use Decoder for all operations.
      fsIn = File.OpenRead(".\characters.bin")
      br = New BinaryReader(fsIn)
      Dim decoder As Decoder = enc.GetDecoder()
      Dim chars(50) As Char
      Dim index As Integer = 0         ' Next character to write in array.
      Dim written As Integer = 0       ' Number of chars written to array.
      Do 
         read = br.Read(bytesRead, 0, count)
         If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then 
            Array.Resize(chars, chars.Length + 50)
         End If   
         written = decoder.GetChars(bytesRead, 0, read, chars, index)
         index += written                          
      Loop While read = count
      br.Close()            
      ' Instantiate a string with the decoded characters.
      Dim str3 As New String(chars, 0, index) 
      Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...")
      CompareForEquality(str1, str3) 
   End Sub

   Private Sub CompareForEquality(original As String, decoded As String)
      Dim result As Boolean = original.Equals(decoded)
      Console.WriteLine("original = decoded: {0}", 
                        original.Equals(decoded, StringComparison.Ordinal))
      If Not result Then
         Console.WriteLine("Code points in original string:")
         For Each ch In original
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next
         Console.WriteLine()

         Console.WriteLine("Code points in decoded string:")
         For Each ch In decoded
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next
         Console.WriteLine()
      End If
   End Sub
End Module
' The example displays the following output:
'    Created original string...
'    
'    Decoded string using UnicodeEncoding.GetString()...
'    original = decoded: False
'    Code points in original string:
'    0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
'    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
'    0020 0073 0020 0062 0308
'    Code points in decoded string:
'    0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
'    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
'    0020 0073 0020 0062 0308
'    
'    Decoded string using UnicodeEncoding.Decoder.GetString()...
'    original = decoded: True
using System;
using System.IO;
using System.Text;

public class Example
{
   public static void Main()
   {
      // Use default replacement fallback for invalid encoding.
      UnicodeEncoding enc = new UnicodeEncoding(true, false, false);

      // Define a string with various Unicode characters.
      string str1 = "AB YZ 19 \uD800\udc05 \u00e4"; 
      str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308"; 
      Console.WriteLine("Created original string...\n");

      // Convert string to byte array.                     
      byte[] bytes = enc.GetBytes(str1);

      FileStream fs = File.Create(@".\characters.bin");
      BinaryWriter bw = new BinaryWriter(fs);
      bw.Write(bytes);
      bw.Close();

      // Read bytes from file.
      FileStream fsIn = File.OpenRead(@".\characters.bin");
      BinaryReader br = new BinaryReader(fsIn);

      const int count = 10;            // Number of bytes to read at a time. 
      byte[] bytesRead = new byte[10]; // Buffer (byte array).
      int read;                        // Number of bytes actually read. 
      string str2 = String.Empty;      // Decoded string.

      // Try using Encoding object for all operations.
      do { 
         read = br.Read(bytesRead, 0, count);
         str2 += enc.GetString(bytesRead, 0, read); 
      } while (read == count);
      br.Close();
      Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...");
      CompareForEquality(str1, str2);
      Console.WriteLine();

      // Use Decoder for all operations.
      fsIn = File.OpenRead(@".\characters.bin");
      br = new BinaryReader(fsIn);
      Decoder decoder = enc.GetDecoder();
      char[] chars = new char[50];
      int index = 0;                   // Next character to write in array.
      int written = 0;                 // Number of chars written to array.
      do { 
         read = br.Read(bytesRead, 0, count);
         if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length) 
            Array.Resize(ref chars, chars.Length + 50);

         written = decoder.GetChars(bytesRead, 0, read, chars, index);
         index += written;                          
      } while (read == count);
      br.Close();            
      // Instantiate a string with the decoded characters.
      string str3 = new String(chars, 0, index); 
      Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...");
      CompareForEquality(str1, str3); 
   }

   private static void CompareForEquality(string original, string decoded)
   {
      bool result = original.Equals(decoded);
      Console.WriteLine("original = decoded: {0}", 
                        original.Equals(decoded, StringComparison.Ordinal));
      if (! result) {
         Console.WriteLine("Code points in original string:");
         foreach (var ch in original)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
         Console.WriteLine();

         Console.WriteLine("Code points in decoded string:");
         foreach (var ch in decoded)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//    Created original string...
//    
//    Decoded string using UnicodeEncoding.GetString()...
//    original = decoded: False
//    Code points in original string:
//    0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
//    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
//    0020 0073 0020 0062 0308
//    Code points in decoded string:
//    0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
//    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
//    0020 0073 0020 0062 0308
//    
//    Decoded string using UnicodeEncoding.Decoder.GetString()...
//    original = decoded: True

Fallback 전략 선택

메서드로 문자를 인코딩 또는 디코딩하려고 시도할 때 매핑이 존재하지 않으면 실패한 매핑을 처리할 방법을 결정하는 fallback 전략을 구현해야 합니다. fallback 전략은 다음과 같은 세 가지 유형이 있습니다.

  • 최적 문자 fallback

  • 대체 fallback

  • 예외 fallback

중요중요

인코딩 작업 시 가장 일반적인 문제는 유니코드 문자를 특정 코드 페이지 인코딩으로 매핑할 수 없는 경우에 발생합니다.디코딩 작업 시 가장 일반적인 문제는 잘못된 바이트 시퀀스를 올바른 유니코드 문자로 변환할 수 없는 경우에 발생합니다.따라서 특정 인코딩 개체에 사용되는 fallback 전략이 무엇인지 알아야 합니다.가능한 한 모든 경우에 개체를 인스턴스화할 때 인코딩 개체에서 사용되는 fallback 전략을 지정해야 합니다.

최적 문자 fallback

문자가 대상 인코딩에서 정확하게 일치하지 않는 경우 인코더가 문자를 비슷한 문자로 매핑하려고 시도할 수 있습니다. 최적 문자 fallback은 대부분의 경우 디코딩이 아닌 인코딩과 관련된 문제입니다. 유니코드로 매핑할 수 없는 문자가 포함된 코드 페이지는 거의 없습니다. 최적 문자 fallback은 Encoding.GetEncoding(Int32)Encoding.GetEncoding(String) 오버로드로 검색되는 더블바이트 문자 집합 인코딩 및 코드 페이지의 기본값입니다.

참고참고

이론적으로 .NET Framework에서 제공되는 유니코드 인코딩 클래스는(UTF8Encoding, UnicodeEncodingUTF32Encoding) 모든 문자 집합의 모든 문자를 지원하므로 최적 문자 fallback 문제를 없앨 수 있습니다.

최적 문자 전략은 코드 페이지에 따라 달라지지만 여기에서는 이에 대해 자세히 설명하지 않습니다. 예를 들어 일부 코드 페이지에서는 전자 라틴 문자가 보다 일반적인 반자 라틴 문자로 매핑되지만 다른 코드 페이지에서는 그렇지 않습니다. 적극적인 최적 문자 전략에서도 일부 인코딩에서는 일부 문자에 가장 적합한 문자가 없는 경우가 있습니다. 예를 들어 중국어 표의 문자는 코드 페이지 1252로 적절히 매핑되지 않으므로 대체 문자열이 사용됩니다. 기본적으로 이 문자열은 단일 QUESTION MARK(U+003F)입니다.

다음 예제에서는 코드 페이지 1252(서부 유럽 언어에 대한 Windows 코드 페이지)를 사용하여 최적 문자 매핑과 이에 대한 결점을 보여 줍니다. Encoding.GetEncoding(Int32) 메서드는 코드 페이지 1252에 대한 인코딩 개체를 검색하는 데 사용됩니다. 기본적으로 여기에서는 지원되지 않는 유니코드 문자에 대한 최적 문자 매핑이 사용됩니다. 이 예제에서는 ASCII가 아닌 공백으로 구분된 문자 세 개 즉, CIRCLED LATIN CAPITAL LETTER S(U+24C8), SUPERSCRIPT FIVE(U+2075) 및 INFINITY(U+221E)가 포함된 문자열을 인스턴스화합니다. 이 예의 결과에서 보여 지듯이 문자열을 인코딩하면 세 개의 공백이 아닌 원래 문자가 QUESTION MARK(U+003F), DIGIT FIVE(U+0035) 및 DIGIT EIGHT(U+0038)로 바뀝니다. DIGIT EIGHT는 지원되지 않는 INFINITY 문자에 대해 적합하지 않은 대체 문자이며, QUESTION MARK는 원래 문자에 대해 사용 가능한 매핑이 없음을 나타냅니다.

Imports System.Text

Module Example
   Public Sub Main()
      ' Get an encoding for code page 1252 (Western Europe character set).
      Dim cp1252 As Encoding = Encoding.GetEncoding(1252)

      ' Define and display a string.
      Dim str As String = String.Format("{0} {1} {2}", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E))
      Console.WriteLine("Original string: " + str)
      Console.Write("Code points in string: ")
      For Each ch In str
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
      Next
      Console.WriteLine()   
      Console.WriteLine()

      ' Encode a Unicode string.
      Dim bytes() As Byte = cp1252.GetBytes(str)
      Console.Write("Encoded bytes: ")
      For Each byt In bytes
         Console.Write("{0:X2} ", byt)
      Next
      Console.WriteLine()
      Console.WriteLine()

      ' Decode the string.
      Dim str2 As String = cp1252.GetString(bytes)
      Console.WriteLine("String round-tripped: {0}", str.Equals(str2))
      If Not str.Equals(str2) Then
         Console.WriteLine(str2)
         For Each ch In str2
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next
      End If
   End Sub
End Module
' The example displays the following output:
'       Original string: Ⓢ ⁵ ∞
'       Code points in string: 24C8 0020 2075 0020 221E
'       
'       Encoded bytes: 3F 20 35 20 38
'       
'       String round-tripped: False
'       ? 5 8
'       003F 0020 0035 0020 0038
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      // Get an encoding for code page 1252 (Western Europe character set).
      Encoding cp1252 = Encoding.GetEncoding(1252);

      // Define and display a string.
      string str = "\u24c8 \u2075 \u221e";
      Console.WriteLine("Original string: " + str);
      Console.Write("Code points in string: ");
      foreach (var ch in str)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");   

      // Encode a Unicode string.
      Byte[] bytes = cp1252.GetBytes(str);
      Console.Write("Encoded bytes: ");
      foreach (byte byt in bytes)
         Console.Write("{0:X2} ", byt);
      Console.WriteLine("\n");

      // Decode the string.
      string str2 = cp1252.GetString(bytes);
      Console.WriteLine("String round-tripped: {0}", str.Equals(str2));
      if (! str.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
      }
   }
}
// The example displays the following output:
//       Original string: Ⓢ ⁵ ∞
//       Code points in string: 24C8 0020 2075 0020 221E
//       
//       Encoded bytes: 3F 20 35 20 38
//       
//       String round-tripped: False
//       ? 5 8
//       003F 0020 0035 0020 0038

최적 문자 매핑은 유니코드 데이터를 코드 페이지 데이터로 인코딩하는 Encoding 개체의 기본 동작이며, 이 동작을 사용하는 레거시 응용 프로그램이 있습니다. 그러나 대부분의 새 응용 프로그램에서는 보안상의 이유로 최적 문자 매핑 동작을 사용하지 않아야 합니다. 예를 들어 응용 프로그램에서 최적 문자 인코딩을 통한 도메인 이름을 사용해서는 안 됩니다.

참고참고

또한 인코딩을 위해 사용자 지정 최적 문자 fallback 매핑을 구현할 수도 있습니다.자세한 내용은 사용자 지정 fallback 전략 구현 단원을 참조하십시오.

최적 문자 fallback이 인코딩 개체의 기본값인 경우 Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) 또는 Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) 오버로드를 호출하여 Encoding 개체를 검색할 때 다른 fallback 전략을 선택할 수 있습니다. 다음 단원에는 별표(*)를 사용하여 코드 페이지 1252로 매핑할 수 없는 각 문자를 대체하는 예가 포함되어 있습니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim cp1252r As Encoding = Encoding.GetEncoding(1252, 
                                         New EncoderReplacementFallback("*"),
                                         New DecoderReplacementFallback("*"))

      Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
      Console.WriteLine(str1)
      For Each ch In str1
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
      Next    
      Console.WriteLine()

      Dim bytes() As Byte = cp1252r.GetBytes(str1)
      Dim str2 As String = cp1252r.GetString(bytes)
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
      If Not str1.Equals(str2) Then
         Console.WriteLine(str2)
         For Each ch In str2
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next    
         Console.WriteLine()
      End If 
   End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       Round-trip: False
'       * * *
'       002A 0020 002A 0020 002A
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding cp1252r = Encoding.GetEncoding(1252, 
                                  new EncoderReplacementFallback("*"),
                                  new DecoderReplacementFallback("*"));

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine();

      byte[] bytes = cp1252r.GetBytes(str1);
      string str2 = cp1252r.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      } 
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       Round-trip: False
//       * * *
//       002A 0020 002A 0020 002A

대체 fallback

문자가 대상 체계에서 정확하게 일치하지 않지만 매핑할 수 있는 적합한 문자가 없으면 응용 프로그램이 대체 문자 또는 문자열을 지정할 수 있습니다. 이러한 동작은 유니코드 디코더의 기본 동작으로서 디코딩할 수 없는 모든 2바이트 시퀀스를 REPLACEMENT_CHARACTER(U+FFFD)로 바꿉니다. 또한 ASCIIEncoding 클래스의 기본 동작으로서 인코딩하거나 디코딩할 수 없는 각 문자를 물음표로 바꿉니다. 다음 예제에서는 이전 예에서 유니코드 문자열에 대한 대체 문자를 설명합니다. 결과에서 보여 지듯이 ASCII 바이트 값으로 디코딩할 수 없는 각 문자는 물음표에 해당하는 ASCII 코드인 0x3F로 대체됩니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim enc As Encoding = Encoding.Ascii

      Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
      Console.WriteLine(str1)
      For Each ch In str1
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
      Next    
      Console.WriteLine()
      Console.WriteLine() 

      ' Encode the original string using the ASCII encoder.
      Dim bytes() As Byte = enc.GetBytes(str1)
      Console.Write("Encoded bytes: ")
      For Each byt In bytes
         Console.Write("{0:X2} ", byt)
      Next
      Console.WriteLine()
      Console.WriteLine()

      ' Decode the ASCII bytes.
      Dim str2 As String = enc.GetString(bytes)
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
      If Not str1.Equals(str2) Then
         Console.WriteLine(str2)
         For Each ch In str2
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next    
         Console.WriteLine()
      End If 
   End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       
'       Encoded bytes: 3F 20 3F 20 3F
'       
'       Round-trip: False
'       ? ? ?
'       003F 0020 003F 0020 003F
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding enc = Encoding.ASCII;

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");

      // Encode the original string using the ASCII encoder.
      byte[] bytes = enc.GetBytes(str1);
      Console.Write("Encoded bytes: ");
      foreach (var byt in bytes)
         Console.Write("{0:X2} ", byt);
      Console.WriteLine("\n");

      // Decode the ASCII bytes.
      string str2 = enc.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      } 
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       
//       Encoded bytes: 3F 20 3F 20 3F
//       
//       Round-trip: False
//       ? ? ?
//       003F 0020 003F 0020 003F

.NET Framework에는 문자가 인코딩 또는 디코딩 작업에서 정확하게 매핑되지 않는 경우 대체 문자열을 구성하는 EncoderReplacementFallbackDecoderReplacementFallback 클래스가 포함됩니다. 기본적으로 이 대체 문자열은 물음표이지만 클래스 생성자 오버로드를 호출하여 다른 문자열을 선택할 수 있습니다. 일반적으로 대체 문자열은 단일 문자열이지만, 반드시 단일 문자열이어야 하는 것은 아닙니다. 다음 예제에서는 대체 문자열로 별표(*)를 사용하는 EncoderReplacementFallback 개체를 인스턴스화하여 코드 페이지 1252 인코더의 동작을 변경합니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim cp1252r As Encoding = Encoding.GetEncoding(1252, 
                                         New EncoderReplacementFallback("*"),
                                         New DecoderReplacementFallback("*"))

      Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
      Console.WriteLine(str1)
      For Each ch In str1
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
      Next    
      Console.WriteLine()

      Dim bytes() As Byte = cp1252r.GetBytes(str1)
      Dim str2 As String = cp1252r.GetString(bytes)
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
      If Not str1.Equals(str2) Then
         Console.WriteLine(str2)
         For Each ch In str2
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next    
         Console.WriteLine()
      End If 
   End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       Round-trip: False
'       * * *
'       002A 0020 002A 0020 002A
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding cp1252r = Encoding.GetEncoding(1252, 
                                  new EncoderReplacementFallback("*"),
                                  new DecoderReplacementFallback("*"));

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine();

      byte[] bytes = cp1252r.GetBytes(str1);
      string str2 = cp1252r.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      } 
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       Round-trip: False
//       * * *
//       002A 0020 002A 0020 002A
참고참고

또한 인코딩의 대체 클래스를 구현할 수도 있습니다.자세한 내용은 사용자 지정 fallback 전략 구현 단원을 참조하십시오.

특히 유니코드 문자로 성공적으로 변환할 수 없는 바이트 시퀀스를 디코딩할 때에는 IQUESTION MARK(U+003F) 외에도 유니코드 REPLACEMENT CHARACTER(U+FFFD)가 일반적으로 대체 문자열로 사용됩니다. 하지만 어떤 대체 문자열이라도 자유롭게 선택할 수 있으며 이러한 대체 문자열에 여러 문자가 포함될 수도 있습니다.

예외 fallback

최적 문자 fallback 또는 교체 문자열을 제공하는 대신, 문자 집합을 인코딩할 수 없으면 인코더가 EncoderFallbackException을 throw할 수 있으며, 바이트 배열을 디코딩할 수 없으면 디코더가 DecoderFallbackException을 throw할 수 있습니다. 인코딩 및 디코딩 작업에서 예외를 throw하려면 EncoderExceptionFallback 개체 및 DecoderExceptionFallback 개체를 각각 Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) 메서드에 제공합니다. 다음 예제에서는 ASCIIEncoding 클래스에서의 예외 fallback을 보여 줍니다.

Imports System.Text

Module Example
   Public Sub Main()
      Dim enc As Encoding = Encoding.GetEncoding("us-ascii", 
                                                 New EncoderExceptionFallback(), 
                                                 New DecoderExceptionFallback())

      Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
      Console.WriteLine(str1)
      For Each ch In str1
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
      Next    
      Console.WriteLine()
      Console.WriteLine() 

      ' Encode the original string using the ASCII encoder.
      Dim bytes() As Byte = {}
      Try
         bytes = enc.GetBytes(str1)
         Console.Write("Encoded bytes: ")
         For Each byt In bytes
            Console.Write("{0:X2} ", byt)
         Next
         Console.WriteLine()
      Catch e As EncoderFallbackException
         Console.Write("Exception: ")
         If e.IsUnknownSurrogate() Then
            Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.", 
                              Convert.ToUInt16(e.CharUnknownHigh), 
                              Convert.ToUInt16(e.CharUnknownLow), 
                              e.Index)
         Else
            Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.", 
                              Convert.ToUInt16(e.CharUnknown), 
                              e.Index)
         End If                              
         Exit Sub
      End Try
      Console.WriteLine()

      ' Decode the ASCII bytes.
      Try
         Dim str2 As String = enc.GetString(bytes)
         Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
         If Not str1.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
               Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next    
            Console.WriteLine()
         End If 
      Catch e As DecoderFallbackException
         Console.Write("Unable to decode byte(s) ")
         For Each unknown As Byte In e.BytesUnknown
            Console.Write("0x{0:X2} ")
         Next
         Console.WriteLine("at index {0}", e.Index)
      End Try
   End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       
'       Exception: Unable to encode 0x24C8 at index 0.
using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding enc = Encoding.GetEncoding("us-ascii", 
                                          new EncoderExceptionFallback(), 
                                          new DecoderExceptionFallback());

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");

      // Encode the original string using the ASCII encoder.
      byte[] bytes = {};
      try {
         bytes = enc.GetBytes(str1);
         Console.Write("Encoded bytes: ");
         foreach (var byt in bytes)
            Console.Write("{0:X2} ", byt);

         Console.WriteLine();
      }
      catch (EncoderFallbackException e) {
         Console.Write("Exception: ");
         if (e.IsUnknownSurrogate())
            Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.", 
                              Convert.ToUInt16(e.CharUnknownHigh), 
                              Convert.ToUInt16(e.CharUnknownLow), 
                              e.Index);
         else
            Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.", 
                              Convert.ToUInt16(e.CharUnknown), 
                              e.Index);
         return;
      }
      Console.WriteLine();

      // Decode the ASCII bytes.
      try {
         string str2 = enc.GetString(bytes);
         Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
         if (! str1.Equals(str2)) {
            Console.WriteLine(str2);
            foreach (var ch in str2)
               Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

            Console.WriteLine();
         } 
      }
      catch (DecoderFallbackException e) {
         Console.Write("Unable to decode byte(s) ");
         foreach (byte unknown in e.BytesUnknown)
            Console.Write("0x{0:X2} ");

         Console.WriteLine("at index {0}", e.Index);
      }
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       
//       Exception: Unable to encode 0x24C8 at index 0.
참고참고

또한 인코딩 작업을 위해 사용자 지정 예외 처리기를 구현할 수도 있습니다.자세한 내용은 사용자 지정 fallback 전략 구현 단원을 참조하십시오.

EncoderFallbackExceptionDecoderFallbackException 개체는 예외를 일으킨 조건에 대해 다음과 같은 정보를 제공합니다.

EncoderFallbackExceptionDecoderFallbackException 개체는 예외에 대한 적절한 진단 정보를 제공하지만 인코딩 또는 디코딩 버퍼에 대한 액세스를 제공하지 않습니다. 따라서 인코딩 또는 디코딩 메서드 내에서 잘못된 데이터가 바뀌거나 수정될 수 없습니다.

사용자 지정 fallback 전략 구현

코드 페이지에서 내부적으로 구현되는 최적 문자 매핑 외에도 .NET Framework에는 fallback 전략 구현을 위해 다음과 같은 클래스가 포함됩니다.

또한 다음과 같은 단계에 따라 최적 문자 fallback, 교체 fallback 또는 예외 fallback을 사용하는 사용자 지정 솔루션을 구현할 수 있습니다.

  1. 인코딩 작업의 경우 EncoderFallback에서, 디코딩 작업의 경우 DecoderFallback에서 클래스를 파생합니다.

  2. 인코딩 작업의 경우 EncoderFallbackBuffer에서, 디코딩 작업의 경우 DecoderFallbackBuffer에서 클래스를 파생합니다.

  3. 예외 fallback의 경우 미리 정의된 EncoderFallbackExceptionDecoderFallbackException 클래스가 사용자의 요구에 부합되지 않으면 Exception 또는 ArgumentException과 같은 예외 개체에서 클래스를 파생합니다.

EncoderFallback 또는 DecoderFallback에서 파생

사용자 지정 fallback 솔루션을 구현하려면 인코딩 작업의 경우 EncoderFallback에서, 디코딩 작업의 경우 DecoderFallback에서 파생되는 클래스를 만들어야 합니다. 이러한 클래스의 인스턴스는 Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) 메서드에 전달되며, 인코딩 클래스와 fallback 구현 간의 매개자로 작동합니다.

인코더 또는 디코더에 대해 사용자 지정 fallback 솔루션을 만들 때는 다음과 같은 멤버를 구현해야 합니다.

EncoderFallbackBuffer 또는 DecoderFallbackBuffer에서 파생

사용자 지정 fallback 솔루션을 구현하려면 인코딩 작업의 경우 EncoderFallbackBuffer에서, 디코딩 작업의 경우 DecoderFallbackBuffer에서 파생되는 클래스도 만들어야 합니다. 이러한 클래스의 인스턴스는 EncoderFallbackDecoderFallback 클래스의 CreateFallbackBuffer 메서드에 의해 반환됩니다. EncoderFallback.CreateFallbackBuffer 메서드는 인코더가 인코딩할 수 없는 첫 번째 문자를 발견한 경우 인코더에 의해 호출되며, DecoderFallback.CreateFallbackBuffer 메서드는 디코더가 디코딩할 수 없는 하나 이상의 바이트를 발견한 경우 디코더에 의해 호출됩니다. EncoderFallbackBufferDecoderFallbackBuffer 클래스는 fallback 구현을 제공합니다. 각 인스턴스는 인코딩 할 수 없는 문자 또는 디코딩할 수 없는 바이트 시퀀스를 대신하는 fallback 문자가 포함된 버퍼를 제공합니다.

인코더 또는 디코더에 대해 사용자 지정 fallback 솔루션을 만들 때는 다음과 같은 멤버를 구현해야 합니다.

fallback 구현이 최적 문자 fallback이거나 대체 fallback인 경우 EncoderFallbackBufferDecoderFallbackBuffer에서 파생된 클래스는 두 개의 전용 인스턴스 필드인 버퍼에 포함된 정확한 문자 수 및 버퍼에서 반환할 다음 문자의 인덱스가 유지 관리됩니다.

EncoderFallback 예

이전 예에서는 ASCII 문자에 해당하지 않는 유니코드 문자를 별표(*)로 바꾸는 대체 fallback이 사용되었습니다. 다음 예제에서는 그 대신 사용자 지정 최적 문자 fallback 구현을 사용하여 ASCII가 아닌 문자에 대해 더 효과적인 매핑을 제공합니다.

다음 코드에서는 ASCII가 아닌 문자에 대한 최적 문자 매핑을 처리하기 위해 EncoderFallback에서 파생된 CustomMapper라는 클래스를 정의합니다. 해당 CreateFallbackBuffer 메서드는 EncoderFallbackBuffer 구현을 제공하는 CustomMapperFallbackBuffer 개체를 반환합니다. CustomMapper 클래스는 Dictionary<TKey, TValue> 개체를 사용하여 지원되지 않는 유니코드 문자(키 값) 및 해당 8비트 문자(64비트 정수로 된 두 개의 연속되는 바이트로 저장됨)의 매핑을 저장합니다. 이 매핑을 fallback 버퍼에 제공하기 위해서는 CustomMapper 인스턴스가 CustomMapperFallbackBuffer 클래스 생성자에 대한 매개 변수로 전달됩니다. 유니코드 문자 U+221E에서 가장 긴 매핑은 문자열 "INF"이므로 MaxCharCount 속성이 3을 반환합니다.

Public Class CustomMapper : Inherits EncoderFallback
   Public DefaultString As String
   Friend mapping As Dictionary(Of UShort, ULong)

   Public Sub New()
      Me.New("?")
   End Sub

   Public Sub New(ByVal defaultString As String)
      Me.DefaultString = defaultString

      ' Create table of mappings
      mapping = New Dictionary(Of UShort, ULong)
      mapping.Add(&H24C8, &H53)
      mapping.Add(&H2075, &H35)
      mapping.Add(&H221E, &H49004E0046)
   End Sub

   Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer
      Return New CustomMapperFallbackBuffer(Me)
   End Function

   Public Overrides ReadOnly Property MaxCharCount As Integer
      Get
         Return 3
      End Get
   End Property
End Class
public class CustomMapper : EncoderFallback
{
   public string DefaultString;
   internal Dictionary<ushort, ulong> mapping;

   public CustomMapper() : this("*")
   {   
   }

   public CustomMapper(string defaultString)
   {
      this.DefaultString = defaultString;

      // Create table of mappings
      mapping = new Dictionary<ushort, ulong>();
      mapping.Add(0x24C8, 0x53);
      mapping.Add(0x2075, 0x35);
      mapping.Add(0x221E, 0x49004E0046);
   }

   public override EncoderFallbackBuffer CreateFallbackBuffer()
   {
      return new CustomMapperFallbackBuffer(this);
   }

   public override int MaxCharCount
   {
      get { return 3; }
   } 
}

다음 코드에서는 EncoderFallbackBuffer에서 파생되는 CustomMapperFallbackBuffer 클래스를 정의합니다. 최적 문자 매핑을 포함하고 CustomMapper 인스턴스에 정의된 사전은 해당 클래스 생성자에서 제공됩니다. ASCII 인코더가 인코딩할 수 없는 유니코드 문자가 매핑 사전에 정의된 경우 Fallback 메서드가 true를 반환하고, 그렇지 않으면 false를 반환합니다. 각 fallback에서 전용 count 변수는 반환하도록 남은 문자 수를 나타내며 전용 index 변수는 문자열 버퍼 charsToReturn에서 반환할 다음 문자의 위치를 나타냅니다.

Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer

   Dim count As Integer = -1        ' Number of characters to return
   Dim index As Integer = -1        ' Index of character to return
   Dim fb As CustomMapper
   Dim charsToReturn As String

   Public Sub New(ByVal fallback As CustomMapper)
      MyBase.New()
      Me.fb = fallback
   End Sub

   Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean
      ' Do not try to map surrogates to ASCII.
      Return False
   End Function

   Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean
      ' Return false if there are already characters to map.
      If count >= 1 Then Return False

      ' Determine number of characters to return.
      charsToReturn = String.Empty

      Dim key As UShort = Convert.ToUInt16(charUnknown)
      If fb.mapping.ContainsKey(key) Then
         Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))
         Dim ctr As Integer
         For Each byt In bytes
            If byt > 0 Then
               ctr += 1
               charsToReturn += Chr(byt)
            End If
         Next
         count = ctr
      Else
         ' Return default.
         charsToReturn = fb.DefaultString
         count = 1
      End If
      Me.index = charsToReturn.Length - 1

      Return True
   End Function

   Public Overrides Function GetNextChar() As Char
      ' We'll return a character if possible, so subtract from the count of chars to return.
      count -= 1
      ' If count is less than zero, we've returned all characters.
      If count < 0 Then Return ChrW(0)

      Me.index -= 1
      Return charsToReturn(Me.index + 1)
   End Function

   Public Overrides Function MovePrevious() As Boolean
      ' Original: if count >= -1 and pos >= 0
      If count >= -1 Then
         count += 1
         Return True
      Else
         Return False
      End If
   End Function

   Public Overrides ReadOnly Property Remaining As Integer
      Get
         Return If(count < 0, 0, count)
      End Get
   End Property

   Public Overrides Sub Reset()
      count = -1
      index = -1
   End Sub
End Class
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
   int count = -1;                   // Number of characters to return
   int index = -1;                   // Index of character to return
   CustomMapper fb; 
   string charsToReturn; 

   public CustomMapperFallbackBuffer(CustomMapper fallback)
   {
      this.fb = fallback;
   }

   public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
   {
      // Do not try to map surrogates to ASCII.
      return false;
   }

   public override bool Fallback(char charUnknown, int index)
   {
      // Return false if there are already characters to map.
      if (count >= 1) return false;

      // Determine number of characters to return.
      charsToReturn = String.Empty;

      ushort key = Convert.ToUInt16(charUnknown);
      if (fb.mapping.ContainsKey(key)) {
         byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);
         int ctr = 0;
         foreach (var byt in bytes) {
            if (byt > 0) {
               ctr++;
               charsToReturn += (char) byt;
            }
         }
         count = ctr;
      }
      else {
         // Return default.
         charsToReturn = fb.DefaultString;
         count = 1;
      }
      this.index = charsToReturn.Length - 1;

      return true;
   }

   public override char GetNextChar()
   {
      // We'll return a character if possible, so subtract from the count of chars to return.
      count--;
      // If count is less than zero, we've returned all characters.
      if (count < 0) 
         return '\u0000';

      this.index--;
      return charsToReturn[this.index + 1];
   }

   public override bool MovePrevious()
   {
      // Original: if count >= -1 and pos >= 0
      if (count >= -1) {
         count++;
         return true;
      }
      else {
         return false;
      }
   }

   public override int Remaining 
   {
      get { return count < 0 ? 0 : count; }
   }

   public override void Reset()
   {
      count = -1;
      index = -1;
   }
}

그런 후 다음 코드에서는 CustomMapper 개체를 인스턴스화하고 개체의 인스턴스를 Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) 메서드에 전달합니다. 결과에는 최적 문자 fallback 구현으로 원래 문자열에 있는 세 개의 ASCII가 아닌 문자가 성공적으로 처리된 것으로 표시됩니다.

Imports System.Text
Imports System.Collections.Generic

Module Module1

   Sub Main()
      Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New DecoderExceptionFallback())

      Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&H24C8), ChrW(&H2075), ChrW(&H221E))
      Console.WriteLine(str1)
      For ctr As Integer = 0 To str1.Length - 1
         Console.Write("{0} ", Convert.ToUInt16(str1(ctr)).ToString("X4"))
         If ctr = str1.Length - 1 Then Console.WriteLine()
      Next
      Console.WriteLine()

      ' Encode the original string using the ASCII encoder.
      Dim bytes() As Byte = enc.GetBytes(str1)
      Console.Write("Encoded bytes: ")
      For Each byt In bytes
         Console.Write("{0:X2} ", byt)
      Next
      Console.WriteLine()
      Console.WriteLine()

      ' Decode the ASCII bytes.
      Dim str2 As String = enc.GetString(bytes)
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
      If Not str1.Equals(str2) Then
         Console.WriteLine(str2)
         For Each ch In str2
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
         Next
         Console.WriteLine()
      End If
   End Sub
End Module
using System;
using System.Collections.Generic;
using System.Text;

class Program
{
   static void Main()
   {
      Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {
         Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));
         if (ctr == str1.Length - 1) 
            Console.WriteLine();
      }
      Console.WriteLine();

      // Encode the original string using the ASCII encoder.
      byte[] bytes = enc.GetBytes(str1);
      Console.Write("Encoded bytes: ");
      foreach (var byt in bytes)
         Console.Write("{0:X2} ", byt);

      Console.WriteLine("\n");

      // Decode the ASCII bytes.
      string str2 = enc.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      }
   }
}

참고 항목

참조

Encoder

Decoder

DecoderFallback

Encoding

EncoderFallback

기타 리소스

인코딩 및 지역화

변경 기록

날짜

변경 내용

이유

2010년 10월

광범위하게 수정되었습니다.

향상된 기능 관련 정보