C/C++ 관련 포스팅 목록
2020/07/03 - [Linux/C] - C/C++ 문자열 Hex 값 출력(16진수 변환)
2020/07/03 - [Linux/C] - C/C++ printf 포멧 API 사용 예제(출력) - 1
2020/07/02 - [Linux/C] - C/C++ C에서 Split 구현 예제(문자열 자르기 strtok)
2020/07/01 - [Linux/C] - C/C++ API 문자열 특정 문자로 나누기(strtok)
2020/06/28 - [Linux/C] - C/C++ API strchr(특정 문자 위치 검색)
2020/06/27 - [Linux/C] - C/C++ API access(파일 존재 여부 확인)
목차
리눅스 우분투 C 언어 개발 환경 구성 예제 관련 이전 포스팅
오늘 Hex 문자열 바이트 변환 예제를 따라하기에 앞서 리눅스 환경에서 C언어 개발 환경을 아직 구성하지 않았다면 아래 링크의 이전 포스팅을 참고해주세요. Codelite를 설치하면 됩니다.
2023.07.28 - [C] - [C/C++] Openssl 정적 라이브러리 빌드 및 Codelite 설치, 개발 환경 구성(ubuntu, codelite)
API의 필요성
안녕하세요 오늘 포스팅에서는 Hex 문자열을 바이트 배열로 변환하는 방법에 대하여 알아보겠습니다. 이전 포스팅에서는 문자열의 값들을 0xAB 형식으로 헥스 문자열로 변환하여 출력하는 방법에 대해 확인하였습니다. 오늘은 이와 반대로 "AB10CB7A39" 와 같이 16진수 문자열이 있다고 가정할 때, 이 문자열을 unsigned char[] 배열 즉, 바이트 배열로 변환하여 저장하는 예제를 확인해보겠습니다.
우리는 한 형태를 다른 형태로 변환하는 과정을 컨버팅이라고 합니다. 프로그램을 개발하다보면 문자열을 헥스로 출력 하거나 헥스를 바이트 배열로 변환하는 유틸리티 함수들이 많이 필요합니다. 똑같은 행위를 매번 다시 처리하는 것은 비효율적이기 때문에 이러한 많이 사용하는 유틸리티 함수를 한번 만들어놓고 다른 프로젝트에서 재사용한다면 정말 효율적이겠죠?
여러분들이 이 포스팅을 읽고 다른 프로젝트를 진행하거나 제 블로그의 다른 예제들을 따라할 때도 이 내용을 잘 이해하고 재사용한다면 분명 좋은 시간이 될 것 같습니다.
함수 구조 설명
#include <stdio.h>
#include <stdint.h>
#include <string.h>
unsigned char *parseBytes(const char *hex_str);
# 인자
const char *hex_str
- Hex 문자열 포인터를 의미한다.
- 예를들어 "ABAB56333C1A8001A8" 라는 일련의 Hex 문자열을 의미한다.
# 반환
unsigned char *
- 동적으로 할당된 unsigned char *(부호 없는 char 변수) 포인터를 반환한다.
- 이 버퍼에는 바이트 값이 저장되어 있다.
"ABAB56333C1A8001A8" 라는 Hex 문자열은 곧 0xAB, 0xAB, 0x56, 0x33 .... 의 16진수 값들을 의미합니다. 또한 1Byte의 기억 공간은 0xFF 까지 표현할 수 있기 때문에 부혹 없는 unsigned char 형을 사용해야 합니다.
변환해 성공하였을 때 반환되는 Bytes 포인터는 동적으로 할당(Malloc)된 버퍼이기 때문에 반드시 사용이 끝난 이후에는 Free API를 사용하여 리소스 해제를 진행해야 합니다.
예제 목표
이번 포스팅에서의 목표는 char, unsigned char의 차이점이 부호 1비트가 있느냐 없느냐의 차이라는 것을 이해하는 것입니다. 1Byte는 8bit이고 부호를 표시하기 위해서는 1bit를 사용해야 하기 때문에 값을 표현하는데 7bit밖에 쓸 수 없습니다.
이럴 경우 0xFF 까지 표현하지 못하게 됩니다. 따라서 unsigned char형을 사용하여 부호비트를 사용하지 않고 전체 8bit를 값을 표시하는데 사용합니다. 또한 매번 포스팅에서 중요시하는 문자열의 끝에 NULL을 사용하여 정상 문자열 종료를 나타내는 방법을 다시 한번 이해하겠습니다.
코드 작성
$ cd /tmp
$ mkdir hex_to_bytes_example; cd hex_to_bytes_example
$ vim hex_to_bytes_test.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
unsigned char *parseBytes(const char *hex_str) {
char tmp[3];
int i;
int hex_str_len = strlen(hex_str);
unsigned char *bytes = NULL;
// 만약 문자열 길이가 2의 배수가 아니라면 비정상 헥스 문자열이므로 NULL 반환
if ((hex_str_len % 2) != 0) return NULL;
bytes = (unsigned char *)malloc(hex_str_len / 2);
if (!bytes) return NULL;
// hex 문자열 2글자를 임시저장하여 16진수값으로 변환하고 bytes에 저장
for (i = 0; i < hex_str_len / 2; i++) {
memcpy(tmp, hex_str + (i * 2), 2);
tmp[2] = 0;
bytes[i] = (unsigned char)strtoul(tmp, NULL, 16);
}
return bytes;
}
void main() {
unsigned char *bytes;
const char *hex_string = "ABAB56333C1A8001A8";
const char *hex_string2 = "C8A2334";
// hex_string Hex 문자열 값 변환
printf("hex_string - Hex String length : %d\n", strlen(hex_string));
bytes = parseBytes(hex_string);
if (!bytes) printf("bytes failed to convert\n\n");
else printf("bytes convert success - 1,2,3 element value : %02X, %02X, %02X\n\n", bytes[0], bytes[1], bytes[2]);
// hex_string2 Hex 문자열 값 변환
printf("hex_string2 - Hex String length : %d\n", strlen(hex_string2));
bytes = parseBytes(hex_string2);
if (!bytes) printf("bytes failed to convert\n\n");
else printf("bytes convert success - 1,2,3 element value : %02X, %02X, %02X\n\n", bytes[0], bytes[1], bytes[2]);
}
예제 프로그램은 만약 Hex 문자열의 길이가 2의 배수로 끝나지 않는 비정상 문자열을 입력 받았을 경우는 NULL을 반환하여 예외처리하고 있습니다.
또한 strtoul API를 사용하여 두개의 문자를 16진수 값으로 파싱하고 있습니다. 이러한 알고리즘을 잘 이해한다면 편리하게 사용할 수 있습니다.
실행
$ cd /tmp/hex_to_bytes_example
$ gcc -c hex_to_bytes_test.c -o hex_to_bytes_test.out
$ gcc -o hex_to_bytes_test hex_to_bytes_test.out
$ ./hex_to_bytes_test
결과
위와 같이 비정상 Hex 문자열을 입력 받았을 경우 프로그램의 에러가 발생하고 만약 정상적인 문자열이었다면 바이트 배열이 반환되어 각 원소의 값이 잘 출력되는 것을 알 수 있었습니다.