C# 자주쓰는 타입 변환 - byte, string, int
C# 프로그래밍을 하다 보면 자주 사용하는 데이터 타입 간 변환이 있는데 이것들을 정리해 볼까 한다.
■ 바이트 배열과 문자열 간의 변환
일단은 byte[] 타입과 스트링 간의 변환이다. byte 타입은 통신 과정에서 많이 다루게 되는데 시리얼 통신이나 TCP/IP 통신 과정에서 서버와 클라이언트, 피어 간의 메시지 전달은 통상 byte [] 배열을 사용하는 내장 함수들이 많다. 대표적인 사례를 살펴보면 소켓 클래스의 메시지를 송수신하는 Send(), Receive() 함수를 들 수 있다. byte 배열은 말 그대로 8비트가 모인 1 바이트가 하나의 원소인 배열로 그 안에 포함된 내용이 영문인지, 한글인지, 문자인지 이진 값인지 상관하지 않는다.
반면에 문자열 스트링은 사람이 인식하는 텍스트를 가지고 있는 오브젝트로 문자 하나하나에 대한 인식이 가능하다. 예를 들어 "국어ABC"라는 str_a라는 스트링이 있을 때 str_a[0]는 char 타입의 "국"을 가리키고 str_a[2]는 "A"를 가리킨다. str_a의 길이가 5라고 해서 메모리도 5바이트를 점유하는 것은 아니다. 각 인코딩 방식에 따라 한 문자를 표현하기 위한 메모리가 1바이트에서 최대 4바이트가 될 수도 있기 때문이다. 그렇지만 이 스트링을 바이트 배열에 담으면 더 이상 각 문자 단위 해석은 불가능해진다. str_a를 바이트 배열 byte_a에 저장한 다음 byte_a[0]를 보면 234이고, byte_a[2]는 173이며 영문 'A'는 byte_a[6]에 저장되어 있다. 물론 이런 문자 값 배치는 문자열의 인코딩에 따라 달라지는데 대표적으로 많이 사용하는 인코딩이 UTF-8이고, 윈도 시스템에서 사용하는 인코딩은 "EUC-KR"이고 엄밀히 말하면 "CP949"이다.
그러므로 바이트 배열과 문자열간의 변환에서는 반드시 인코딩을 감안하여 작업해야 한다. 영숫자만이 있다면 문제는 단순하지만 한글이나 기타 글로벌한 문자가 포함되는 경우라면 인코딩을 확인해서 작업해야만 한다.
byte[] byte_a = Encoding.UTF8.GetBytes(str_a);
string str_b = Encoding.UTF8.GetString(byte_a);
위의 코드는 스트링 문자열을 UTF8 인코딩 형태의 바이트 배열로 전환하는 과정과 바이트 배열의 내용을 UTF8 인코딩으로 해석하여 스트링 문자열로 전환하는 예제이다. Encoding 클래스를 사용하려면 using System.Text; 참조를 추가해 주어야 한다. 위의 예제에서는 인코딩으로 가장 많이 사용하는 UTF8을 사용했지만 Default, ASCII, Unicode, UTF7, UTF32, Unicode, BigEndianUnicode 등도 적용할 수 있다. 시스템에서 기본적으로 제공하는 인코딩 외의 인코딩은 Encoding.GetEncoding()으로 호출하여 사용할 수 있는데 이전에 많이 사용하던 EUC-KR 한글 인코딩은 코드 페이지 값을 사용하는 Encoding.GetEncoding(51949) 방식이나 인코딩 문자열을 사용하는 Encoding.GetEncoding("euc-kr") 방식으로 호출할 수 있다.
Encoding euckr = Encoding.GetEncoding(51949); //Encoding.GetEncoding("euc-kr") 도 동일
string euckr_str = euckr.GetString(Encoding.Convert(Encoding.UTF8, euckr, Encoding.UTF8.GetBytes(utf_str)));
string utf_str = Encoding.UTF8.GetString(Encoding.Convert(euckr, Encoding.UTF8, euckr.GetBytes(euckr_str)));
위의 예제는 스트링 간의 인코딩 변환을 나타낸 것이다. 중간에 바이트로 변환 과정이 반드시 들어가고, Encoding.Convert()를 사용하는 것에 주목할 필요가 있다.
byte[] cvtint = BitConverter.GetBytes(intvar);
intvar = BitConverter.ToUInt32(tmp4, 0);
그렇다면, 정수나 실수 값은 어떻게 바이트 스트림으로 전달할 수 있을까? 이때 사용할 수 있는 것이 System.BitConverter 클래스이다. 먼저 BitConverter.GetBytes()는 인수 전달한 스칼라 데이터 타입(bool, int 등등)의 값을 바이트 배열로 변환한다. BitConverter.ToUInt32(배열, 배열의 데이터 시작 위치)로 바이트 배열에 있는 값을 스칼라 데이터 타입으로 전환할 수 있는데 시작 위치는 배열 내 인덱스로 위의 예제에서 0으로 지정한 것은 배열의 처음이 시작 위치라는 의미다. 통신으로 대량의 이진 값을 주고받는 다면 이런 형태로 인덱스를 옮겨가며 통신 스트림에 있는 내용을 내부 자료형으로 전환할 수 있는 것이다. 배열에서 데이터 변환에 사용하는 크기는 지정한 데이터 타입에 따르므로 통신 규약에 정의된 크기와 이진 값의 배열 방식(인디언, Endian)에 따라 정확하게 처리해야 한다.
■ 문자열과 숫자 간의 변환
문자열과 숫자간의 변환은 빈번하게 사용하는 테크닉 중의 하나이다. 먼저 숫자 값을 문자열로 변환하는 과정은 변수명.ToString()로 간편하게 처리할 수 있다. 문제는 결과 문자열을 프로그래머의 의도대로 형식화해야 할 필요가 있는 경우이다. 여러 값을 한 번에 형식화할 경우에는 String.Format(출력 형식, 값...)의 형태를 사용한다. 실수 변수 dbla를 소수점 3자리가 나오도록 출력하고 싶다면 String.Format("{0:0.000}", dbla)로 호출한다.
dbla.ToString("0.############"); //소수점을 최대한 나오도록 하고 싶은 경우
dbla.ToString("0.000"); //소수점 3자리
dbla.ToString("#,##0;@#,##0;---"); //양수형식;음수형식;0일때 형식
dbla.ToString("N2"); //콤마를 포함한 숫자 형식(소수점 2자리)
inta.ToString("X8"); //8자리 16진수 스트링(정수 변수에 적용)
위의 코드들은 많이 사용하는 예제를 나열한 것이다.
반대로 문자열을 숫자로 변환하는 과정은 단순하기는 한데 한 가지 주의할 점은 변환 가능한 문자열인가 하는 점이다. 정수로 변환하려는데 유효 범위를 넘어서거나, 영문자가 섞여 있으면 예외를 발생시킬 수 있기 때문이다. 예외를 무시하도록 처리해 놓으면 되지만 중요한 점은 이런 예외를 감안한 프로그래밍이 되어야 한다는 것이다.
예외의 가능성이 없는 경우라면 Convert.ToInt32(str); 처럼 Convert 클래스를 활용해서 원하는 스칼라 데이터 타입으로 간편하게 변환할 수 있다. 만약 문자열이 2진수, 8진수, 16진수 문자열이라면 Convert.ToInt32(str, 16)처럼 진수 값을 지정해서 정수로 변환할 수 있다.
문자열을 숫자로 변환하는 과정에서 예외가 발생하지 않도록 하려면 데이터 타입. TryParse()를 활용한다.
int tmp_n;
if (!int.TryParse(str_a, out tmp_n)) tmp_n = 0;
위의 예제는 문자열 str_a를 int 타입을 변환하여 tmp_n에 저장하도록 TryParse()를 호출하고 변환에 실패한 경우에는 0을 기본값으로 설정하는 과정을 나타낸 것이다.