IT_Programming/C#

C# StreamReader 에서의 한글 Encoding 문제

JJun ™ 2010. 12. 30. 09:25



출처: http://www.digipine.com/programming/4337/page/1



 

string fileContent = null;
using ( StreamReader sr = new StreamReader( filePath ) )
{
      fileContent = sr.ReadToEnd();
      sr.Close();
}

.NET 에서는 문자열 처리를 명시적으로 지정하지 않는 한, 기본적으로 "System.Text.UTF8Encoding" 으로 처리를 합니다. 

문제는 거기서 발생을 하지요. 해당 HTML 텍스트 한글 파일은 메모장에서 "ASCII" 형식으로 저장된 것이었고, 디코딩을 UTF-8 로 해버리니 

당연히 깨질 수 밖에 없습니다.

한글이 포함된 ASCII 코드를 정상적으로 읽어들이기 위해서는 인코딩을 지정해야 합니다. 

StreamReader 의 두번째 인자에는 바로 그 인코딩 방식을 지정할 수가 있죠. 

우리가 아는 것처럼 "KS_C_5601-1987" 인코딩 방식을 지정해야 합니다. 


다음과 같은 코드로.

using ( StreamReader sr = new StreamReader( filePath, Encoding.GetEncoding( "ks_c_5601-
1987" ) ) )

{
      fileContent = sr.ReadToEnd();

      sr.Close();
}

명시적인 Encoding 문자열 지정대신에, Encoding.Default 를 지정해도 됩니다.

시스템 레벨로 설정된 (제어판의 Regional Settings) code page 값이 한글 윈도우즈에서는 기본적으로 "KS_C_5601-1987" 이기 때문입니다.


하지만, 여기서 끝이 아니죠. 만약 해당 파일이 utf-8 또는 unicode 로 인코딩된 텍스트 파일이라면? 

당연히 위의 코드로 읽어들이게 되면 역시 한글이 깨지게 됩니다.

Unicode 또는 UTF-8 등의 텍스트 파일은 그 인코딩 방법을 표시하기 위해, 파일의 최초 2~3 바이트에 BOM(byte order mark)를 표시해 둡니다. 

utf-8 로 인코딩된 텍스트 파일을 윈도우즈의 "메모장"으로 열어보면 그러한 표시를 생략하고 순수 텍스트만 보여주지만, 

2진 파일 형식으로 볼 수 있는 hexa 에디터등을 통해서 보게 되면, 최초 2~3바이트의 내용이 인코딩에 따라서 달라지는 것을 확인할 수 있습니다.


             UTF-8: EF BB BF
             Unicode: FF FE


실제로, BOM 을 통한 디코딩을 지원하지 않는 Editor 로 UTF-8 인코딩된 파일을 열게 되면,

최초 3byte 를 깨진 텍스트로 출력해 주는 것을 볼 수 있습니다.

아뭏든... 그렇다면, 우리도 BOM 을 읽어서 상황에 따라 StreamReader 의 2번째 인자에
각각 해당하는 인코딩을 넣어주면 되겠지요. 아마도 C++ 이었다면 틀림없이 그렇게 해야 했을 것입니다.
하지만, 우리의 "친절한 .NET 씨"(이 표현은 Loner(
http://simpleisbest.net)에서 빌려옴) 는

그런 작업을 모두 해놓았습니다. 바로 StreamReader 의 3번째 인자에서 그러한 역할을 대신해 줍니다.

public StreamReader(..., bool detectEncodingFromByteOrderMarks);

매개변수 이름 부터 그런 태생임을 짐작하게 해줍니다.

해당 값을 true 로 전달하면 BOM 마크가 없는 - 예를 들어 ANSI 파일 - 경우에는 2번째 인자에서

지정한 Encoding 방식으로 인식해서 디코딩을 하지만, BOM 이 있으면 거기서 지정된 Encoding

방식으로 되어 있다고 인식해서 디코딩을 하게 됩니다.

그러니... 앞으로 국내에서의 .NET 개발자들은 텍스트 파일을 로딩하기 위한 표준 코드를

다음과 같이 해야 합니다.

using ( StreamReader sr = new StreamReader( filePath, Encoding.GetEncoding( "ks_c_5601-1987" ), true ) )

{
      fileContent = sr.ReadToEnd();
      sr.Close();
}

또는

using ( StreamReader sr = new StreamReader( filePath, Encoding.Default, true ) )
{
      fileContent = sr.ReadToEnd();
      sr.Close();
}

두 가지 모두, 경우에 따라서 논란의 여지가 있지만...
적어도 한글 윈도우즈에서 호스팅 된다는 
보장만 된다면, "Encoding.Default" 인자가 가장 좋을 것 같네요.

 

 


 

 

[ BOM 제거 버전 ]

 

using System;
using System.IO;
using System.Text;

 

class MainClass

{
    static void Main()

    {
        using (FileStream fs = new FileStream("test.txt", FileMode.Create))

        {
            using (StreamWriter w = new StreamWriter(fs, new UTF8Encoding(false)))

            {
                w.WriteLine(124.23M);
                w.WriteLine("Test string");
                w.WriteLine('!');
            }
        }


        using (FileStream fs = new FileStream("test.txt", FileMode.Open))

        {
            using (StreamReader r = new StreamReader(fs, Encoding.UTF8, true))

            {
                Console.WriteLine(Decimal.Parse(r.ReadLine()));
                Console.WriteLine(r.ReadLine());
                Console.WriteLine(Char.Parse(r.ReadLine()));
            }
        }
    }
}