Post List

2015년 9월 7일 월요일

C# (.NET) 에서 File 의 BOM 여부 상관없이 characterset(Unicode, Ansi) 확인하기

C# (.NET)에서 File 을 읽을 때 characterset을 제대로 설정해주지 않으면 정상적으로 읽을 수 없습니다.

BOM 이 있는 File인 경우에는 자동으로 읽을 수 있는 옵션을 제공해줍니다.

public StreamReader(
 Stream stream,
 bool detectEncodingFromByteOrderMarks
)

하지만 BOM이 없는 경우에는 딱히 자동으로 확인 할 수 있는 방법이 없습니다.
그래서 어떻게 확인 가능한 방법이 없을까 검색하던중
Notepad에서 characterset을 확인하는 방법을 소개한 blog를 발견하였습니다.

http://blogs.msdn.com/b/oldnewthing/archive/2004/03/24/95235.aspx

예를 들어서 "Hello" 가 저장되어 있는 Text File인 경우 각각의 characterset 및 BOM 여부에 따라서 아래와 같이 저장됩니다.

* Traditional ANSI encoding

     48 65 6C 6C 6F

* Unicode (little-endian) encoding without BOM

     48 00 65 00 6C 00 6C 00 6F 00

* Unicode (little-endian) encoding with BOM

     FF FE 48 00 65 00 6C 00 6C 00 6F 00

* Unicode (big-endian) encoding without BOM

     00 48 00 65 00 6C 00 6C 00 6F

* Unicode (big-endian) encoding with BOM

     FE FF 00 48 00 65 00 6C 00 6C 00 6F

* UTF-8 encoding with BOM

     EF BB BF 48 65 6C 6C 6F

* UTF-7 encoding with BOM

     2B 2F 76 38 2D 48 65 6C 6C 6F

위 7가지에 대해서 Check를 자동으로 하여 File 내용을 읽어주는 함수를 만들어 봤습니다.
Encoding에 맞게 읽으면 왠만해서는 그대로 사용이 가능하지만,
사용하는 Platform에 따라 안되는 파일이 있어서 다시한번 Convert 하는 Code를 넣었습니다.

* Code

using System.IO;
using System.Text;
public string ReadFile(string fileName, Encoding encode = null)
{
if (encode == null)
encode = Encoding.Default;
string strRet = null;
Encoding fileEnc = DetectEncoding(fileName);
using (Stream S = File.OpenRead(fileName))
{
using (StreamReader sr = new StreamReader(S, fileEnc))
{
if (encode == fileEnc)
strRet = sr.ReadToEnd();
else
{
string strFile = sr.ReadToEnd();
byte[] bytesFile = fileEnc.GetBytes(strFile);
byte[] bytesRet = Encoding.Convert(fileEnc, encode, bytesFile);
strRet = encode.GetString(bytesRet);
}
}
}
return strRet;
}
public Encoding DetectEncoding(string filename)
{
using (Stream S = File.OpenRead(filename))
{
using (StreamReader SR = new StreamReader(S))
{
char[] buffer = new char[10];
SR.Read(buffer, 0, 10);
if (IsEqualBytes(buffer, new byte[] { 0xFF, 0xFE }, 2))
{
return Encoding.Unicode; // Little Endian with BOM
}
else if (IsEqualBytes(buffer, new byte[] { 0xFE, 0xFF }, 2))
{
return Encoding.BigEndianUnicode; // with BOM
}
else if (IsEqualBytes(buffer, new byte[] { 0xEF, 0xBB, 0xBF }, 3))
{
return Encoding.UTF8; // with BOM
}
else if (IsEqualBytes(buffer, new byte[] { 0x2B, 0x2F, 0x76, 0x38, 0x2D }, 5))
{
return Encoding.UTF7; // with BOM
}
else if (IsZeroBytes(buffer, 10, true))
{
return Encoding.Unicode; // Little Endian without BOM
}
else if (IsZeroBytes(buffer, 10, false))
{
return Encoding.BigEndianUnicode; // without BOM
}
}
}
return Encoding.Default; // Ansi : MBCS (Multibyte Character Set)
}
public bool IsEqualBytes(char[] src, byte[] pattern, int num)
{
if (src.Length < num || pattern.Length < num)
return false; //throw new ArgumentOutOfRangeException();
bool bRet = true;
for (int i = 0; i < num; i++)
{
if (src[i] != pattern[i])
{
bRet = false;
break;
}
}
return bRet;
}
public bool IsZeroBytes(char[] src, int num, bool checkOdd = true)
{
if (src.Length < num)
return false; //throw new ArgumentOutOfRangeException();
bool bRet = true;
int rest = checkOdd ? 1 : 0;
for (int i = 0; i < num; i++)
{
if (i % 2 == rest)
{
if (src[i] != 0x00)
{
bRet = false;
break;
}
}
}
return bRet;
}

댓글 없음:

댓글 쓰기