-
[WEEK07] C언어에서 문자열 다루기. 근데 이제 포인터와 파싱(parsing)을 곁들인SW Jungle/TIL (Today I Learned) 2022. 11. 10. 22:36
SW정글에서 7주차 과제인 웹 프록시 서버를 만들며, HTTP 통신의 기초를 공부하게 되었다.
여기서는 HTTP 통신에 대해서는 다루지 않고, 전달된 값을 분석하는 과정인 문자열 파싱(parsing)을 다뤄보려 한다.
1. C언어에서의 문자와 문자열
가. 문자열의 선언
기본적으로 C에서는 문자를 아스키 코드값으로 다룬다.
아스키 코드가 127까지 있으므로, 문자 하나는 char 자료형 (1 Byte) 크기에 담을 수 있다.
그래서 일반적으로 문자열은 char 배열로 선언한다.
문자열의 맨 마지막에는 널 문자가 들어가기 때문에, 문자열 배열의 크기는 문자열의 길이 + 1 로 설정해 주어야 한다.
* 널 문자 == 숫자
0
=='\0'
==NULL
* 문자
'0'
은 널 문자가 아님에 주의하라.✚
한글은 문자 하나 당 3바이트를 차지한다.
왜 그럴까? 궁금하다면, 세계의 다양한 문자들을 통일된 방법으로 저장하기 위한 포맷인 UTF-8에 대해 더 알아보면 좋다. 이 사이트에 간략한 정리가 잘 되어 있다.나. 문자열의 구조
C언어에서는 문자열의 시작과 끝을 어떻게 알고 인식할까?
결론부터 말하면, 문자열은 첫 글자가 위치한 바이트의 주소에서부터 널 문자를 만나기까지의 연속된 바이트들이다.
문자열을 전달하기 위해서는, 위의 코드에서 보았듯 문자열이 담긴 배열을 주면 된다.
배열을 준다는 말은 곧 배열 첫 주소의 포인터를 전달한다는 의미이다.
그리고 '문자열의 끝은 널 문자로 한다'는 사실이 약속되어 있다.
우리는 이런 문자열의 구조를 적절히 이용해서 문자열을 우리가 원하는대로 쪼개어 사용할 수 있다.
2. 문자열 파싱이란 무엇인가?
파싱을 우리말로 풀어 쓰면 구문 분석이다.
즉, 주어진 문자열을 적절히 활용하기 위해서 가공하고 추출하는 기법인 것이다.
예를 들어
"name : gildong, age : 30, hobby : soccer"
와 같은 문자열이 있다고 해보자.우리는 이 문자열에서 이름과 나이와 취미를 추출하여 구조체로 관리하고 싶다.
주어진 문자열은 쉼표를 기준으로
"속성 : 값"
형태를 갖고 있다."name : gildong"
"age : 30"
"hobby : soccer"
그리고 다시 콜론을 기준으로 속성과 값을 나눌 수 있다.
"name"
"gildong"
"age"
"30"
"hobby"
"soccer"
이제 원하는 값으로 추출되었으므로, 해당 값을 이용해서 구조체를 만들 수 있게 되었다.
3. C언어의 문자열 함수
고맙게도 C언어는 <string.h> 파일에 꽤 강력하고 다양한 문자열 처리 함수를 제공하고 있다.
그 중에 자주 쓰이는 몇 가지 함수를 소개하겠다.
함수에 대한 자세한 정보를 제공하는 IBM의 공식 문서 링크를 각 함수의 제목에 걸어 놓았다.
탐색
char *strchr (const char *string, int c);
문자열 string에서 문자 하나 c를 찾는다. 해당 문자가 처음 등장하는 위치의 주소를 반환한다.
해당 문자열을 끝까지 탐색(널 문자 '\0'를 만날때까지)했는데도 찾지 못했다면 NULL을 반환한다.
char *strstr (const char *string1, const char *string2);
문자열 string1에서 문자열 string2가 처음 등장하는 위치를 찾고 그 첫 부분의 주소를 반환한다.
strchr 함수와 마찬가지로 찾지 못한다면 NULL을 반환한다.
비교
int strcmp (const char *string1, const char *string2);
문자열 string1과 문자열 string2를 비교해서, 같으면 0을 반환한다.
같지 않으면 양수 또는 음수를 반환한다.
양수 또는 음수가 나오는 조건은, 문자열 string1과 string2에서 처음으로 다른 값이 나오는 부분의 대소 관계에 따라 달라진다.
string1가 더 크면 1, string2가 더 크면 -1이 반환된다.
예를 들어 "apple"과 "application"이라는 문자열을 비교하면,
처음으로 다른 문자가 나오는 부분은 'e'와 'i'이다.
이때 'e'의 아스키코드는 101 이고 'i'의 아스키코드는 105 이므로, i가 더 크다.
처음 다른 값이 나오는 부분에서 b_문자열의 문자가 더 큰 값을 가지므로, -1이 반환되게 된다.
int strcasecmp (const char *string1, const char *string2);
strcmp 함수와 같은 기능을 한다.
다만 모든 영문자를 소문자로 변환한 후에 비교한다. (대소문자를 구분하지 않는다)
복사
char *strcpy (char *string1, const char *string2);
문자열 string2를 string1의 위치로 복사하고, string1의 주소를 리턴한다.
이 함수는 배열의 길이를 따로 검사하지 않는다.
그러므로 목적지 string1의 배열 길이가 소스 string2의 길이보다 짧지는 않은지 신경써야 한다.
이를 보완하기 위한 함수인 strncpy() 함수가 존재한다.
char *strcat (char *string1, const char *string2);
문자열 string2를 문자열 string1의 뒤에 연결해준다. 그리고 string1의 주소를 리턴한다.
이 함수 역시 길이를 따로 검사하지 않기 때문에, 보완을 위해서는 strncat() 함수를 사용하면 된다.
길이
size_t strlen (const char *string);
문자열 string의 길이를 반환한다.
문자열의 끝에 들어가는 널 문자는 길이에 포함되지 않는다.
변환
형변환은 문자열을 위한 함수는 아니지만, 문자열 파싱에 쓰이기 때문에 여기서 다루겠다.
<stdlib.h> 파일에 정의되어 있다.
int atoi (const char *string);
문자열 string을 정수값으로 변환해서 반환한다.
문자열 "2400"은 숫자 2400이 아니기 때문에, 숫자로 사용하려면 형변환을 해주어야 한다.
그럴 때 이 함수를 쓰면 된다.
4. 미세 팁
작은 따옴표는 문자 하나, 큰 따옴표는 문자열을 감쌀 때 사용한다.
'a' 는 문자 하나이다.
char a = 'apple' 을 하게 되면, 마지막 문자인 'e'만 변수 a에 저장된다.
"a"는 문자열이다. 그러므로 a 뒤에는 널 문자가 붙어서, "a\0"의 값을 가지게 된다.
그러므로 char a = "a"를 하면 잘못된 대입이다.
char a[2] = "a"와 같이 대입해야 올바른 대입이다.
'SW Jungle > TIL (Today I Learned)' 카테고리의 다른 글
[WEEK09~10] syscall wait()의 구현 - 구조 설계를 중점으로 (Pintos PROJECT2 : USER PROGRAMS) (0) 2022.11.29 [WEEK08] Pintos PROJECT1 : THREADS (0) 2022.11.17 [WEEK06] Malloc Lab 설명서 번역 (CS:APP) (2) 2022.10.28 [WEEK05] C언어로 구현하는 레드-블랙 트리 (Red-Black Tree) (1) 2022.10.27 [WEEK04/DAY02] 백준 9084번: 동전 (0) 2022.10.15