C 언어 문자열 처리 함수 개요
C 언어에서 문자열은 주로 문자 배열 형태로 다루어지며, 다양한 문자열 처리 작업을 위해 표준 라이브러리(<string.h> 및 <stdlib.h> 등)에서 여러 유용한 함수를 제공합니다. 이러한 함수들은 문자열의 입출력, 연결, 복사, 비교, 검색, 길이 측정 및 타입 변환 등 광범위한 기능을 수행합니다. 이 문서에서는 C 언어의 주요 문자열 처리 함수들을 자세히 살펴보겠습니다.
1. 입출력 함수
1.1.1. puts 함수
puts 함수는 주어진 문자열을 표준 출력(콘솔)으로 내보내고, 문자열 끝의 널 종료 문자(\0)를 줄 바꿈 문자(\n)로 대체합니다. 따라서 문자열을 출력한 후 자동으로 다음 줄로 넘어갑니다.
#include <stdio.h> // puts 함수를 위해 필요
int main() {
char greeting[] = "안녕하세요, C 프로그래밍!";
puts(greeting); // "안녕하세요, C 프로그래밍!" 출력 후 줄바꿈
char message[] = "새로운 줄입니다.";
puts(message); // "새로운 줄입니다." 출력 후 줄바꿈
return 0;
}
1.1.2. gets 함수
gets 함수는 표준 입력에서 한 줄의 문자를 읽어들여 지정된 문자 배열에 저장합니다. 입력된 줄의 마지막 엔터 키(\n)는 널 종료 문자(\0)로 대체됩니다. 이 함수의 반환 값은 입력된 문자열의 시작 주소입니다.
#include <stdio.h> // gets 함수를 위해 필요
int main() {
char userInput[100]; // 사용자 입력을 저장할 배열
printf("문자열을 입력하세요: ");
gets(userInput); // 표준 입력에서 문자열을 읽어옴
printf("입력된 문자열: %s\n", userInput);
return 0;
}
주의: gets 함수는 입력 버퍼의 크기를 확인하지 않으므로, 사용자 입력이 배열의 크기를 초과할 경우 버퍼 오버플로우가 발생할 수 있습니다. 이는 심각한 보안 취약점으로 이어질 수 있으므로, gets 함수 사용은 강력히 권장되지 않습니다. 대신, 버퍼 크기를 지정할 수 있는 fgets 함수를 사용하는 것이 안전합니다.
2. 문자열 연결 함수
2.1.1. strcat 함수
strcat 함수는 두 문자열을 연결하는 기능을 수행합니다. 원형은 char *strcat(char *dest, const char *src); 이며, src 문자열의 내용을 dest 문자열의 끝에 추가합니다. 연결된 결과는 dest에 저장되며, dest의 시작 주소를 반환합니다. dest 배열은 src의 내용을 포함하기에 충분한 크기여야 합니다.
#include <stdio.h>
#include <string.h> // strcat 함수를 위해 필요
int main() {
char mainBuffer[50] = "Program";
const char* suffix = " is Fun!";
printf("연결 전: %s\n", mainBuffer); // Program
strcat(mainBuffer, suffix);
printf("연결 후: %s\n", mainBuffer); // Program is Fun!
// 여러 문자열 연결 예시
char finalString[100] = ""; // 반드시 초기화 필요
const char* part1 = "Hello";
const char* part2 = " ";
const char* part3 = "World";
strcat(finalString, part1);
strcat(finalString, part2);
strcat(finalString, part3);
printf("최종 문자열: %s\n", finalString); // Hello World
return 0;
}
주의: dest 배열의 크기가 연결될 문자열 전체를 담을 수 있도록 충분히 커야 합니다. 그렇지 않으면 버퍼 오버플로우가 발생합니다.
2.1.2. strncat 함수
strncat 함수는 strcat과 유사하지만, src 문자열에서 연결할 최대 문자 수를 지정할 수 있습니다. 원형은 char *strncat(char *dest, const char *src, size_t n); 이며, src의 처음 n개 문자 또는 널 종료 문자까지의 문자 중 더 적은 수를 dest에 연결합니다. 이 함수도 dest의 시작 주소를 반환합니다.
#include <stdio.h>
#include <string.h> // strncat 함수를 위해 필요
int main() {
char primaryBuffer[50] = "C Language ";
const char* additionalText = "Programming is powerful.";
printf("원본 문자열: %s\n", primaryBuffer);
// additionalText의 처음 11개 문자만 연결 ("Programming")
strncat(primaryBuffer, additionalText, 11);
printf("부분 연결 (11자): %s\n", primaryBuffer); // C Language Programming
// additionalText의 남은 부분 중 3개 문자만 더 연결 (" is")
strncat(primaryBuffer, additionalText + 11, 3);
printf("추가 부분 연결 (3자): %s\n", primaryBuffer); // C Language Programming is
return 0;
}
3. 문자열 복사 함수
3.1.1. strcpy 함수
strcpy 함수는 문자열을 복사하는 표준 함수입니다. 원형은 char *strcpy(char *dest, const char *src); 이며, src가 가리키는 문자열(널 종료 문자 포함)을 dest가 가리키는 위치로 복사합니다. dest의 시작 주소를 반환합니다.
#include <stdio.h>
#include <string.h> // strcpy 함수를 위해 필요
int main() {
char targetArray[30];
const char* sourceString = "안녕하세요, 문자열 복사!";
printf("복사 전 targetArray: (초기화되지 않음)\n");
strcpy(targetArray, sourceString);
printf("복사 후 targetArray: %s\n", targetArray); // 안녕하세요, 문자열 복사!
// 복사된 문자열이 원본과 동일한지 확인
if (strcmp(targetArray, sourceString) == 0) {
printf("복사된 내용이 원본과 일치합니다.\n");
} else {
printf("복사된 내용이 원본과 다릅니다.\n");
}
return 0;
}
주의: strcpy도 strcat과 마찬가지로 dest 배열이 src의 내용을 담을 수 있도록 충분한 크기여야 합니다. 버퍼 오버플로우 위험이 있습니다.
3.1.2. strncpy 함수
strncpy 함수는 strcpy와 달리 src 문자열에서 복사할 최대 문자 수를 지정합니다. 원형은 char *strncpy(char *dest, const char *src, size_t n); 입니다. src의 처음 n개 문자를 dest로 복사합니다. 만약 src의 길이가 n보다 작으면, dest의 나머지 공간은 널 문자로 채워집니다. 하지만 src의 길이가 n보다 길 경우, dest는 널 종료되지 않을 수 있으므로, 수동으로 널 종료 문자를 추가하는 것이 안전합니다.
#include <stdio.h>
#include <string.h> // strncpy 함수를 위해 필요
int main() {
char destBuffer[20];
const char* longSource = "Programming in C is fun.";
// longSource의 처음 11개 문자만 복사 ("Programming")
strncpy(destBuffer, longSource, 11);
destBuffer[11] = '\0'; // strncpy는 n까지 복사할 때 널 종료를 보장하지 않으므로 수동으로 추가
printf("부분 복사 (11자): %s\n", destBuffer); // Programming
// longSource의 처음 3개 문자만 복사 ("Pro")
strncpy(destBuffer, longSource, 3);
destBuffer[3] = '\0';
printf("부분 복사 (3자): %s\n", destBuffer); // Pro
// 복사할 n이 원본 문자열 길이보다 길 경우: 나머지 공간은 널로 채워진다.
char smallDest[10];
const char* shortSrc = "Test";
strncpy(smallDest, shortSrc, 9); // Test\0\0\0\0\0
printf("길게 복사 (9자): %s\n", smallDest); // Test
return 0;
}
3.1.3. memcpy 함수
memcpy 함수는 지정된 메모리 블록에서 바이트 단위로 데이터를 복사합니다. 원형은 void *memcpy(void *dest, const void *src, size_t n); 이며, src가 가리키는 메모리에서 n바이트를 dest가 가리키는 메모리로 복사합니다. 이 함수는 문자열뿐만 아니라 모든 종류의 데이터를 복사하는 데 사용될 수 있습니다.
#include <stdio.h>
#include <string.h> // memcpy 함수를 위해 필요
int main() {
char destinationBuffer[30];
const char* sourceData = "RAW_DATA_12345";
printf("복사 전: %s\n", destinationBuffer); // (쓰레기 값 또는 초기화되지 않음)
// sourceData의 8바이트를 destinationBuffer로 복사
memcpy(destinationBuffer, sourceData, 8);
destinationBuffer[8] = '\0'; // 수동 널 종료 추가 (memcpy는 널 종료를 고려하지 않음)
printf("8바이트 복사 후: %s\n", destinationBuffer); // RAW_DATA
int numbers[] = {10, 20, 30, 40, 50};
int copiedNumbers[5];
// int 배열의 처음 3개 요소(3 * sizeof(int) 바이트)를 복사
memcpy(copiedNumbers, numbers, sizeof(int) * 3);
printf("복사된 숫자: %d, %d, %d\n", copiedNumbers[0], copiedNumbers[1], copiedNumbers[2]); // 10, 20, 30
return 0;
}
주의: memcpy는 원본과 대상 메모리 영역이 겹칠 때의 동작은 정의되지 않습니다 (Undefined Behavior). 메모리 영역이 겹칠 가능성이 있다면 memmove를 사용해야 합니다.
#include <stdio.h>
#include <string.h>
int main() {
char data[] = "0123456789";
printf("원본: %s\n", data); // 0123456789
// data + 3 (인덱스 3부터) 위치에 data (인덱스 0부터)의 5바이트를 복사
// 메모리 영역이 겹침: [01234]56789 <- 01234[56789]
// 이 경우 memcpy의 동작은 예측 불가 (Undefined Behavior)
memcpy(data + 3, data, 5);
printf("memcpy(겹침): %s\n", data); // 결과는 컴파일러와 환경에 따라 달라질 수 있음 (예: 0120123489)
return 0;
}
3.1.4. memmove 함수
memmove 함수는 memcpy와 유사하게 메모리 블록을 바이트 단위로 복사하지만, 원본과 대상 메모리 영역이 겹쳐도 안전하게 동작합니다. 원형은 void *memmove(void *dest, const void *src, size_t n); 이며, n바이트를 복사합니다. 내부적으로 임시 버퍼를 사용하거나 복사 방향을 조절하여 겹침 문제없이 올바른 결과를 보장합니다.
#include <stdio.h>
#include <string.h> // memmove 함수를 위해 필요
int main() {
char data[] = "0123456789";
printf("원본: %s\n", data); // 0123456789
// data + 3 (인덱스 3부터) 위치에 data (인덱스 0부터)의 5바이트를 복사
// 메모리 영역이 겹치지만 memmove는 안전하게 처리
memmove(data + 3, data, 5);
printf("memmove(겹침): %s\n", data); // 0120123489 (예측 가능한 결과)
return 0;
}
3.1.5. memset 함수
memset 함수는 지정된 메모리 블록의 처음 n바이트를 특정 값(바이트)으로 채웁니다. 원형은 void *memset(void *s, int c, size_t n); 이며, s가 가리키는 메모리 영역의 처음 n바이트를 c(unsigned char로 변환됨) 값으로 설정합니다. s의 주소를 반환합니다.
#include <stdio.h>
#include <string.h> // memset 함수를 위해 필요
int main() {
char buffer[20];
printf("초기화 전: (쓰레기 값)\n");
memset(buffer, 'A', 10); // buffer의 처음 10바이트를 'A'로 채움
buffer[10] = '\0'; // 문자열로 사용하기 위해 널 종료 추가
printf("10바이트를 'A'로 채운 후: %s\n", buffer); // AAAAAAAAAA
memset(buffer, '*', 5); // buffer의 처음 5바이트를 '*'로 채움
printf("처음 5바이트를 '*'로 채운 후: %s\n", buffer); // *****AAAAA
return 0;
}
3.1.6. strdup 함수
strdup 함수는 주어진 문자열을 복사하여 새로운 메모리 공간에 할당하고, 그 복사본에 대한 포인터를 반환합니다. 이 함수는 동적으로 메모리를 할당하므로, 사용 후에는 반드시 free 함수를 호출하여 메모리를 해제해야 합니다. (strdup는 POSIX 표준 함수이며, 일부 컴파일러에서는 _strdup와 같이 밑줄이 붙은 형태로 제공될 수 있습니다.)
#include <stdio.h>
#include <string.h> // strdup 함수를 위해 필요 (또는 _strdup)
#include <stdlib.h> // malloc, free 함수를 위해 필요
int main() {
const char* originalString = "메모리에 복사될 문자열";
char* duplicatedString;
printf("원본 문자열: %s\n", originalString);
// strdup 또는 _strdup 사용
// duplicatedString = strdup(originalString); // POSIX
duplicatedString = _strdup(originalString); // Visual Studio
if (duplicatedString == NULL) {
perror("메모리 할당 실패");
return 1;
}
printf("복사된 문자열: %s\n", duplicatedString);
// 복사된 문자열이 원본과 동일한지 확인
if (strcmp(originalString, duplicatedString) == 0) {
printf("복사 성공: 내용이 일치합니다.\n");
} else {
printf("복사 실패: 내용이 일치하지 않습니다.\n");
}
free(duplicatedString); // 동적으로 할당된 메모리 해제
duplicatedString = NULL;
return 0;
}
주의: strdup로 할당된 메모리는 반드시 free로 해제해야 합니다. 그렇지 않으면 메모리 누수(memory leak)가 발생합니다.
3.1.7. strset 함수 (비표준)
strset 함수는 주어진 문자열의 모든 문자를 특정 문자로 대체합니다. 이 함수는 표준 C 라이브러리의 일부가 아니며, 주로 MSVC(Microsoft Visual C++)와 같은 특정 컴파일러에서 _strset 형태로 제공됩니다.
#include <stdio.h>
#include <string.h> // _strset 함수를 위해 필요 (비표준)
int main() {
char myData[15] = "Original String";
char fillChar = '#';
printf("변경 전: %s\n", myData); // Original String
// _strset 함수를 사용하여 모든 문자를 fillChar로 대체
_strset(myData, fillChar); // Visual Studio (비표준)
printf("변경 후: %s\n", myData); // ###############
return 0;
}
주의: strset (또는 _strset)은 비표준 함수이므로, 이식성 있는 코드를 작성하려면 직접 루프를 사용하여 문자열을 변경하는 것이 좋습니다.
4. 문자열 비교 함수
4.1.1. strcmp 함수
strcmp 함수는 두 문자열을 비교합니다. 원형은 int strcmp(const char *str1, const char *str2); 이며, 두 문자열을 사전 순서(lexicographical order)로 비교합니다. 비교는 널 종료 문자를 만날 때까지 문자 단위로 ASCII(또는 해당 시스템의 문자 인코딩) 값을 기준으로 이루어집니다.
str1이str2보다 작으면 음수 반환str1이str2와 같으면 0 반환str1이str2보다 크면 양수 반환
주의: C 언어에서 문자열 비교 시 == 연산자를 사용하면 문자열 내용이 아닌 포인터 주소를 비교하게 되므로, 항상 strcmp와 같은 함수를 사용해야 합니다.
#include <stdio.h>
#include <string.h> // strcmp 함수를 위해 필요
int main() {
const char* strA = "Apple";
const char* strB = "Banana";
const char* strC = "apple";
int result1 = strcmp(strA, strB); // "Apple" vs "Banana"
if (result1 < 0) printf("%s < %s\n", strA, strB);
else if (result1 == 0) printf("%s == %s\n", strA, strB);
else printf("%s > %s\n", strA, strB); // 결과: Apple < Banana
int result2 = strcmp(strA, strA); // "Apple" vs "Apple"
if (result2 == 0) printf("%s == %s\n", strA, strA); // 결과: Apple == Apple
int result3 = strcmp(strA, strC); // "Apple" vs "apple" (대소문자 구분)
if (result3 < 0) printf("%s < %s\n", strA, strC); // 결과: Apple < apple (A의 ASCII < a의 ASCII)
else if (result3 == 0) printf("%s == %s\n", strA, strC);
else printf("%s > %s\n", strA, strC);
return 0;
}
4.1.2. strncmp 함수
strncmp 함수는 strcmp와 유사하지만, 비교할 최대 문자 수를 지정합니다. 원형은 int strncmp(const char *str1, const char *str2, size_t n); 이며, str1과 str2의 처음 n개 문자 또는 널 종료 문자가 나타날 때까지 비교합니다.
#include <stdio.h>
#include <string.h> // strncmp 함수를 위해 필요
int main() {
const char* phrase1 = "HelloWorld";
const char* phrase2 = "HelloEarth";
// 처음 5개 문자 비교 ("Hello" vs "Hello")
int cmpResult1 = strncmp(phrase1, phrase2, 5);
if (cmpResult1 == 0) {
printf("처음 5개 문자가 %s와 %s는 동일합니다.\n", phrase1, phrase2); // 동일
} else if (cmpResult1 < 0) {
printf("처음 5개 문자에서 %s가 %s보다 작습니다.\n", phrase1, phrase2);
} else {
printf("처음 5개 문자에서 %s가 %s보다 큽니다.\n", phrase1, phrase2);
}
// 처음 6개 문자 비교 ("HelloW" vs "HelloE")
int cmpResult2 = strncmp(phrase1, phrase2, 6);
if (cmpResult2 == 0) {
printf("처음 6개 문자가 %s와 %s는 동일합니다.\n", phrase1, phrase2);
} else if (cmpResult2 < 0) {
printf("처음 6개 문자에서 %s가 %s보다 작습니다.\n", phrase1, phrase2); // 결과: HelloWorld가 HelloEarth보다 큽니다 (W > E)
} else {
printf("처음 6개 문자에서 %s가 %s보다 큽니다.\n", phrase1, phrase2);
}
return 0;
}
4.1.3. memcmp 함수
memcmp 함수는 메모리 블록을 바이트 단위로 비교합니다. 원형은 int memcmp(const void *s1, const void *s2, size_t n); 이며, s1과 s2가 가리키는 메모리 영역의 처음 n바이트를 비교합니다. 반환 값은 strcmp와 동일한 규칙을 따릅니다 (음수, 0, 양수).
#include <stdio.h>
#include <string.h> // memcmp 함수를 위해 필요
int main() {
char dataBlock1[] = "XYZabc";
char dataBlock2[] = "XYZABC";
// 처음 3바이트 비교 ("XYZ" vs "XYZ")
int byteCmp1 = memcmp(dataBlock1, dataBlock2, 3);
if (byteCmp1 == 0) {
printf("처음 3바이트는 동일합니다: %s == %s\n", dataBlock1, dataBlock2); // 동일
} else {
printf("처음 3바이트는 다릅니다.\n");
}
// 처음 6바이트 비교 ("XYZabc" vs "XYZABC")
int byteCmp2 = memcmp(dataBlock1, dataBlock2, 6);
if (byteCmp2 == 0) {
printf("처음 6바이트는 동일합니다: %s == %s\n", dataBlock1, dataBlock2);
} else if (byteCmp2 < 0) {
printf("처음 6바이트에서 %s가 %s보다 작습니다.\n", dataBlock1, dataBlock2); // 결과: XYZabc가 XYZABC보다 큽니다 (a > A)
} else {
printf("처음 6바이트에서 %s가 %s보다 큽니다.\n", dataBlock1, dataBlock2);
}
return 0;
}
5. 문자열 검색 함수
5.1.1. memchr 함수
memchr 함수는 주어진 메모리 블록의 처음 n바이트 내에서 특정 문자를 검색합니다. 원형은 void *memchr(const void *s, int c, size_t n); 이며, s가 가리키는 메모리 영역에서 문자 c(unsigned char로 변환됨)의 첫 번째 발생 위치를 찾습니다. 찾으면 해당 위치를 가리키는 포인터를 반환하고, 찾지 못하면 NULL을 반환합니다.
#include <stdio.h>
#include <string.h> // memchr 함수를 위해 필요
int main() {
const char* dataBuffer = "Searching for a character.";
char targetChar = 'c';
char* foundPtr;
printf("데이터 버퍼: \"%s\"\n", dataBuffer);
// 버퍼 전체에서 'c' 검색
foundPtr = (char*)memchr(dataBuffer, targetChar, strlen(dataBuffer));
if (foundPtr != NULL) {
printf("문자 '%c'가 위치 %td에서 처음 발견되었습니다.\n", targetChar, foundPtr - dataBuffer);
printf("발견된 위치부터의 문자열: %s\n", foundPtr); // character.
} else {
printf("문자 '%c'를 찾을 수 없습니다.\n", targetChar);
}
// 제한된 범위 내에서 'z' 검색
targetChar = 'z';
foundPtr = (char*)memchr(dataBuffer, targetChar, 10); // 처음 10바이트 내에서만 검색
if (foundPtr != NULL) {
printf("처음 10바이트 내에서 문자 '%c'가 위치 %td에서 발견되었습니다.\n", targetChar, foundPtr - dataBuffer);
} else {
printf("처음 10바이트 내에서 문자 '%c'를 찾을 수 없습니다.\n", targetChar); // 찾을 수 없음
}
return 0;
}
5.1.2. strchr 함수
strchr 함수는 문자열 내에서 특정 문자의 첫 번째 발생 위치를 검색합니다. 원형은 char *strchr(const char *str, int c); 이며, str 문자열에서 문자 c(unsigned char로 변환됨)의 첫 번째 발생 위치를 가리키는 포인터를 반환합니다. 찾지 못하면 NULL을 반환합니다. 널 종료 문자도 검색될 수 있습니다.
#include <stdio.h>
#include <string.h> // strchr 함수를 위해 필요
int main() {
const char* sampleText = "프로그래밍은 정말 재미있습니다.";
char searchChar1 = '재'; // 멀티바이트 문자도 검색 가능 (시스템 인코딩에 따라)
char searchChar2 = 'X';
char* foundPos;
printf("대상 문자열: \"%s\"\n", sampleText);
// '재' 문자 검색
foundPos = strchr(sampleText, searchChar1);
if (foundPos != NULL) {
printf("문자 '%c'가 위치 %td에서 처음 발견되었습니다.\n", searchChar1, foundPos - sampleText);
printf("발견된 위치부터: %s\n", foundPos); // 재미있습니다.
} else {
printf("문자 '%c'를 찾을 수 없습니다.\n", searchChar1);
}
// 'X' 문자 검색
foundPos = strchr(sampleText, searchChar2);
if (foundPos != NULL) {
printf("문자 '%c'가 위치 %td에서 처음 발견되었습니다.\n", searchChar2, foundPos - sampleText);
} else {
printf("문자 '%c'를 찾을 수 없습니다.\n", searchChar2); // 찾을 수 없음
}
// 널 종료 문자 검색 (문자열 끝)
foundPos = strchr(sampleText, '\0');
if (foundPos != NULL) {
printf("널 종료 문자가 위치 %td에서 발견되었습니다.\n", foundPos - sampleText);
}
return 0;
}
5.1.3. strcspn 함수
strcspn 함수는 문자열의 시작 부분부터, 지정된 문자 집합에 속하는 문자가 처음 나타날 때까지의 길이를 계산합니다. 원형은 size_t strcspn(const char *str1, const char *str2); 이며, str1에서 str2의 어떤 문자도 포함하지 않는 첫 번째 세그먼트의 길이를 반환합니다.
#include <stdio.h>
#include <string.h> // strcspn 함수를 위해 필요
int main() {
const char* mainString = "Welcome home, friend.";
const char* searchSet1 = " o,"; // 공백, 'o', ','
const char* searchSet2 = "xyz";
printf("대상 문자열: \"%s\"\n", mainString);
// searchSet1의 문자가 나타나기 전까지의 길이
size_t length1 = strcspn(mainString, searchSet1);
printf("'%s'에 있는 문자가 나타나기 전까지의 길이: %zu\n", searchSet1, length1); // 결과: 7 (Welcome)
// searchSet2의 문자가 나타나기 전까지의 길이
size_t length2 = strcspn(mainString, searchSet2);
printf("'%s'에 있는 문자가 나타나기 전까지의 길이: %zu\n", searchSet2, length2); // 결과: 21 (문자열 끝까지 아무것도 없음)
return 0;
}
5.1.4. strspn 함수
strspn 함수는 문자열의 시작 부분부터, 지정된 문자 집합에 속하는 문자들로만 구성된 첫 번째 세그먼트의 길이를 계산합니다. 원형은 size_t strspn(const char *str1, const char *str2); 이며, str1에서 str2의 모든 문자를 포함하는 첫 번째 세그먼트의 길이를 반환합니다.
#include <stdio.h>
#include <string.h> // strspn 함수를 위해 필요
int main() {
const char* mixedData = "012345ABCDEF";
const char* digitSet = "0123456789";
const char* alphaSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
printf("대상 문자열: \"%s\"\n", mixedData);
// digitSet의 문자들로만 이루어진 접두사 길이
size_t digitPrefixLength = strspn(mixedData, digitSet);
printf("숫자 접두사의 길이: %zu\n", digitPrefixLength); // 결과: 6 (012345)
// alphaSet의 문자들로만 이루어진 접두사 길이
size_t alphaPrefixLength = strspn(mixedData, alphaSet);
printf("알파벳 접두사의 길이: %zu\n", alphaPrefixLength); // 결과: 0 (첫 문자가 알파벳이 아님)
return 0;
}
5.1.5. strerror 함수
strerror 함수는 시스템 오류 번호에 해당하는 설명 문자열을 반환합니다. 원형은 char *strerror(int errnum); 이며, errnum에 해당하는 오류 메시지를 가리키는 포인터를 반환합니다.
#include <stdio.h>
#include <string.h> // strerror 함수를 위해 필요
#include <errno.h> // errno 매크로 및 오류 코드 정의를 위해 필요
int main() {
// 0부터 몇몇 오류 코드에 대한 메시지 출력
printf("시스템 오류 메시지:\n");
for (int i = 0; i < 15; ++i) {
printf("Error %d: %s\n", i, strerror(i));
}
// 파일 열기 실패 시 errno 활용 예시
FILE* file = fopen("non_existent_file.txt", "r");
if (file == NULL) {
printf("\n파일 열기 실패: %s\n", strerror(errno)); // errno는 마지막 오류 번호를 저장
} else {
fclose(file);
}
return 0;
}
5.1.6. strpbrk 함수
strpbrk 함수는 문자열 내에서 지정된 문자 집합에 속하는 문자가 처음 나타나는 위치를 검색합니다. 원형은 char *strpbrk(const char *str1, const char *str2); 이며, str1에서 str2에 포함된 문자 중 어느 하나라도 처음 나타나는 위치를 가리키는 포인터를 반환합니다. 찾지 못하면 NULL을 반환합니다.
#include <stdio.h>
#include <string.h> // strpbrk 함수를 위해 필요
int main() {
const char* fullSentence = "The year is 2024, a leap year.";
const char* searchChars = "aeiou0123456789"; // 모음 또는 숫자
printf("대상 문자열: \"%s\"\n", fullSentence);
// fullSentence에서 searchChars에 있는 문자를 처음 찾기
char* firstOccurrence = strpbrk(fullSentence, searchChars);
if (firstOccurrence != NULL) {
printf("첫 번째 일치하는 문자: '%c' (위치 %td)\n", *firstOccurrence, firstOccurrence - fullSentence);
printf("해당 위치부터의 문자열: %s\n", firstOccurrence); // e year is 2024, a leap year.
} else {
printf("일치하는 문자를 찾을 수 없습니다.\n");
}
// 다음 일치하는 문자 찾기 (이전 결과의 다음 위치부터 검색)
char* nextOccurrence = strpbrk(firstOccurrence + 1, searchChars);
if (nextOccurrence != NULL) {
printf("다음 일치하는 문자: '%c' (위치 %td)\n", *nextOccurrence, nextOccurrence - fullSentence);
printf("해당 위치부터의 문자열: %s\n", nextOccurrence); // year is 2024, a leap year.
} else {
printf("더 이상 일치하는 문자를 찾을 수 없습니다.\n");
}
return 0;
}
5.1.7. strrchr 함수
strrchr 함수는 문자열 내에서 특정 문자의 마지막 발생 위치를 검색합니다. 원형은 char *strrchr(const char *str, int c); 이며, str 문자열에서 문자 c(unsigned char로 변환됨)의 마지막 발생 위치를 가리키는 포인터를 반환합니다. 찾지 못하면 NULL을 반환합니다. 널 종료 문자도 검색될 수 있습니다.
#include <stdio.h>
#include <string.h> // strrchr 함수를 위해 필요
int main() {
const char* longWord = "programming";
char targetChar1 = 'g';
char targetChar2 = 'z';
char* foundPtr;
printf("대상 문자열: \"%s\"\n", longWord);
// 'g'의 첫 번째 발생 위치
foundPtr = strchr(longWord, targetChar1);
if (foundPtr != NULL) {
printf("문자 '%c'의 첫 번째 위치: %td\n", targetChar1, foundPtr - longWord); // 4
}
// 'g'의 마지막 발생 위치
foundPtr = strrchr(longWord, targetChar1);
if (foundPtr != NULL) {
printf("문자 '%c'의 마지막 위치: %td\n", targetChar1, foundPtr - longWord); // 10
} else {
printf("문자 '%c'를 찾을 수 없습니다.\n", targetChar1);
}
// 'z'의 마지막 발생 위치
foundPtr = strrchr(longWord, targetChar2);
if (foundPtr != NULL) {
printf("문자 '%c'의 마지막 위치: %td\n", targetChar2, foundPtr - longWord);
} else {
printf("문자 '%c'를 찾을 수 없습니다.\n", targetChar2); // 찾을 수 없음
}
return 0;
}
5.1.8. strstr 함수
strstr 함수는 문자열 내에서 다른 문자열(부분 문자열)의 첫 번째 발생 위치를 검색합니다. 원형은 char *strstr(const char *haystack, const char *needle); 이며, haystack 문자열에서 needle 문자열이 처음 나타나는 위치를 가리키는 포인터를 반환합니다. 찾지 못하면 NULL을 반환합니다.
#include <stdio.h>
#include <string.h> // strstr 함수를 위해 필요
int main() {
const char* document = "This is a document about C programming language.";
const char* keyword1 = "programming";
const char* keyword2 = "Python";
char* subStringLocation;
printf("대상 문자열: \"%s\"\n", document);
// "programming" 검색
subStringLocation = strstr(document, keyword1);
if (subStringLocation != NULL) {
printf("'%s'이(가) 위치 %td에서 처음 발견되었습니다.\n", keyword1, subStringLocation - document);
printf("발견된 위치부터: %s\n", subStringLocation); // programming language.
} else {
printf("'%s'을(를) 찾을 수 없습니다.\n", keyword1);
}
// "Python" 검색
subStringLocation = strstr(document, keyword2);
if (subStringLocation != NULL) {
printf("'%s'이(가) 위치 %td에서 처음 발견되었습니다.\n", keyword2, subStringLocation - document);
} else {
printf("'%s'을(를) 찾을 수 없습니다.\n", keyword2); // 찾을 수 없음
}
return 0;
}
5.1.9. strtok 함수
strtok 함수는 문자열을 지정된 구분자(delimiters)를 기준으로 토큰(단어)으로 분리합니다. 원형은 char *strtok(char *str, const char *delim); 이며, str을 delim에 있는 문자들을 구분자로 사용하여 분리합니다. 이 함수는 문자열을 직접 변경(널 종료 문자 삽입)하므로, 원본 문자열이 수정될 수 있다는 점을 유의해야 합니다. 연속적인 호출을 위해 첫 번째 호출 이후에는 str 인수에 NULL을 전달합니다.
#include <stdio.h>
#include <string.h> // strtok 함수를 위해 필요
int main() {
// strtok는 원본 문자열을 수정하므로, const char* 대신 char 배열 사용
char inputLine[] = "Data,Value1;Value2 / Another Data";
const char* separators = ",;/ "; // 쉼표, 세미콜론, 슬래시, 공백을 구분자로 사용
char* token;
printf("원본 문자열: \"%s\"\n\n", inputLine);
printf("분리된 토큰:\n");
// 첫 번째 토큰 분리
token = strtok(inputLine, separators);
while (token != NULL) {
printf(" %s\n", token);
// 다음 토큰을 위해 NULL을 첫 번째 인수로 전달
token = strtok(NULL, separators);
}
return 0;
}
주의: strtok는 내부적으로 정적 변수를 사용하여 상태를 유지하므로, 다중 스레드 환경에서는 strtok_r (재진입 가능 버전)을 사용하는 것이 안전합니다. 또한, 원본 문자열을 수정하므로, 원본 문자열이 필요한 경우 미리 복사해두어야 합니다.
6. 정렬 & 길이 함수
6.1.1. strrev 함수 (비표준)
strrev 함수는 문자열의 순서를 뒤집습니다. 이 함수는 표준 C 라이브러리의 일부가 아니며, 주로 MSVC(Microsoft Visual C++)와 같은 특정 컴파일러에서 _strrev 형태로 제공됩니다.
#include <stdio.h>
#include <string.h> // _strrev 함수를 위해 필요 (비표준)
int main() {
char phrase[] = "Racecar is a palindrome"; // 원본 문자열
printf("뒤집기 전: %s\n", phrase); // Racecar is a palindrome
// _strrev 함수를 사용하여 문자열 뒤집기
_strrev(phrase); // Visual Studio (비표준)
printf("뒤집은 후: %s\n", phrase); // emordnilap a si racecaR
return 0;
}
주의: strrev (또는 _strrev)는 비표준 함수이므로, 이식성 있는 코드를 작성하려면 직접 루프를 사용하여 문자열을 뒤집는 것이 좋습니다.
6.1.2. strlen 함수
strlen 함수는 문자열의 길이를 계산합니다. 원형은 size_t strlen(const char *str); 이며, str이 가리키는 문자열의 널 종료 문자(\0)를 제외한 문자 수를 반환합니다.
#include <stdio.h>
#include <string.h> // strlen 함수를 위해 필요
int main() {
char buffer1[10] = ""; // 빈 문자열
char buffer2[10] = "Hello"; // "Hello"와 널 종료
const char* literalString = "Long string literal"; // 문자열 리터럴
printf("buffer1의 길이: %zu\n", strlen(buffer1)); // 0
printf("buffer2의 길이: %zu\n", strlen(buffer2)); // 5
printf("literalString의 길이: %zu\n", strlen(literalString)); // 19
return 0;
}
7. 대소문자 변환 함수
7.1.1. strlwr 함수 (비표준)
strlwr 함수는 문자열 내의 모든 대문자를 소문자로 변환합니다. 이 함수는 표준 C 라이브러리의 일부가 아니며, 주로 MSVC와 같은 특정 컴파일러에서 _strlwr 형태로 제공됩니다.
#include <stdio.h>
#include <string.h> // _strlwr 함수를 위해 필요 (비표준)
int main() {
char mixedCaseString[] = "HeLlO wOrLd! C PrOgRaMmInG.";
printf("변환 전: %s\n", mixedCaseString); // HeLlO wOrLd! C PrOgRaMmInG.
// _strlwr 함수를 사용하여 대문자를 소문자로 변환
_strlwr(mixedCaseString); // Visual Studio (비표준)
printf("변환 후: %s\n", mixedCaseString); // hello world! c programming.
return 0;
}
주의: strlwr (또는 _strlwr)은 비표준 함수이므로, 이식성 있는 코드를 작성하려면 <ctype.h>의 tolower 함수를 사용하여 직접 변환 루프를 구현하는 것이 좋습니다.
7.1.2. strupr 함수 (비표준)
strupr 함수는 문자열 내의 모든 소문자를 대문자로 변환합니다. 이 함수는 표준 C 라이브러리의 일부가 아니며, 주로 MSVC와 같은 특정 컴파일러에서 _strupr 형태로 제공됩니다.
#include <stdio.h>
#include <string.h> // _strupr 함수를 위해 필요 (비표준)
int main() {
char mixedCaseString[] = "HeLlO wOrLd! c PrOgRaMmInG.";
printf("변환 전: %s\n", mixedCaseString); // HeLlO wOrLd! c PrOgRaMmInG.
// _strupr 함수를 사용하여 소문자를 대문자로 변환
_strupr(mixedCaseString); // Visual Studio (비표준)
printf("변환 후: %s\n", mixedCaseString); // HELLO WORLD! C PROGRAMMING.
return 0;
}
주의: strupr (또는 _strupr)은 비표준 함수이므로, 이식성 있는 코드를 작성하려면 <ctype.h>의 toupper 함수를 사용하여 직접 변환 루프를 구현하는 것이 좋습니다.
8. 다른 데이터 타입으로의 변환 함수
8.1.1. atof 함수
atof 함수는 문자열을 부동 소수점(double) 값으로 변환합니다. 원형은 double atof(const char *str); 이며, 변환된 double 값을 반환합니다. 이 함수는 문자열의 시작 부분에서 유효한 숫자를 파싱하고, 숫자가 아닌 문자를 만나면 변환을 멈춥니다.
#include <stdio.h>
#include <stdlib.h> // atof 함수를 위해 필요
int main() {
const char* floatString1 = "3.14159265";
const char* floatString2 = "123.45abc";
const char* invalidString = "xyz1.0";
double value1, value2, value3;
value1 = atof(floatString1);
printf("문자열 '%s' -> double: %lf\n", floatString1, value1); // 3.141593
value2 = atof(floatString2);
printf("문자열 '%s' -> double: %lf\n", floatString2, value2); // 123.450000 (abc에서 중단)
value3 = atof(invalidString);
printf("문자열 '%s' -> double: %lf\n", invalidString, value3); // 0.000000 (숫자로 시작하지 않음)
return 0;
}
주의: atof는 오류를 상세하게 보고하지 않습니다. 더 강력한 오류 처리 및 기수(base) 지정을 위해서는 strtod 함수를 사용하는 것이 좋습니다.
8.1.2. atoi 함수
atoi 함수는 문자열을 정수(int) 값으로 변환합니다. 원형은 int atoi(const char *str); 이며, 변환된 int 값을 반환합니다. 이 함수도 문자열의 시작 부분에서 유효한 정수를 파싱하고, 숫자가 아닌 문자를 만나면 변환을 멈춥니다.
#include <stdio.h>
#include <stdlib.h> // atoi 함수를 위해 필요
int main() {
const char* intString1 = "12345";
const char* intString2 = "-6789test";
const char* floatString = "98.76";
int value1, value2, value3;
value1 = atoi(intString1);
printf("문자열 '%s' -> int: %d\n", intString1, value1); // 12345
value2 = atoi(intString2);
printf("문자열 '%s' -> int: %d\n", intString2, value2); // -6789 (test에서 중단)
value3 = atoi(floatString);
printf("문자열 '%s' -> int: %d\n", floatString, value3); // 98 (.에서 중단)
return 0;
}
주의: atoi도 오류 처리가 미흡하며, 오버플로우 발생 시 동작이 정의되지 않습니다. strtol을 사용하는 것이 더 안전합니다.
8.1.3. atol 함수
atol 함수는 문자열을 긴 정수(long) 값으로 변환합니다. 원형은 long atol(const char *str); 이며, 변환된 long 값을 반환합니다. atoi와 유사하게 동작하지만, 더 큰 정수 값을 처리할 수 있습니다.
#include <stdio.h>
#include <stdlib.h> // atol 함수를 위해 필요
int main() {
const char* longNumString1 = "1234567890";
const char* longNumString2 = "-9876543210suffix";
long value1, value2;
value1 = atol(longNumString1);
printf("문자열 '%s' -> long: %ld\n", longNumString1, value1); // 1234567890
value2 = atol(longNumString2);
printf("문자열 '%s' -> long: %ld\n", longNumString2, value2); // -9876543210 (suffix에서 중단)
return 0;
}
주의: atol도 오류 처리가 미흡하며, strtol을 사용하는 것이 더 안전합니다.
8.1.4. strtol 함수
strtol 함수는 문자열을 long int 값으로 변환하는 가장 강력하고 유연한 함수입니다. 원형은 long strtol(const char *str, char **endptr, int base); 이며, 다음과 같은 장점이 있습니다.
- 변환이 끝난 문자열의 다음 위치를
endptr을 통해 반환하여 오류 여부와 변환되지 않은 부분을 확인할 수 있습니다. - 변환할 숫자의 기수(base, 2~36)를 지정할 수 있습니다 (0으로 지정하면 접두사에 따라 자동으로 결정).
- 오버플로우/언더플로우 발생 시
LONG_MAX/LONG_MIN을 반환하고errno를 설정합니다.
#include <stdio.h>
#include <stdlib.h> // strtol 함수를 위해 필요
#include <errno.h> // errno를 위해 필요
#include <limits.h> // LONG_MAX, LONG_MIN을 위해 필요
int main() {
char numAndText[] = "FF_Suffix"; // 16진수 FF
char octalNum[] = "017_end"; // 8진수 17
char decimalNum[] = "123456789012345extra"; // 10진수 (long 범위 초과 가능)
char invalidNum[] = "abc123";
char* endPtr;
long parsedValue;
// 16진수 변환
errno = 0; // errno 초기화
parsedValue = strtol(numAndText, &endPtr, 16);
if (errno == ERANGE) {
printf("오버플로우/언더플로우 발생: %s\n", numAndText);
} else if (*endPtr != '\0' && *endPtr != '_') { // 숫자 뒤에 의미 없는 문자가 남았는지 확인
printf("16진수 변환 실패 (남은 문자: '%s'): %s\n", endPtr, numAndText);
} else {
printf("문자열 '%s' (16진수) -> long: %ld (변환 후 남은 문자: '%s')\n", numAndText, parsedValue, endPtr); // 255
}
// 8진수 변환 (base 0: 접두사에 따라 자동 감지)
errno = 0;
parsedValue = strtol(octalNum, &endPtr, 0);
if (errno == ERANGE) {
printf("오버플로우/언더플로우 발생: %s\n", octalNum);
} else if (*endPtr != '\0' && *endPtr != '_') {
printf("8진수 변환 실패 (남은 문자: '%s'): %s\n", endPtr, octalNum);
} else {
printf("문자열 '%s' (8진수) -> long: %ld (변환 후 남은 문자: '%s')\n", octalNum, parsedValue, endPtr); // 15
}
// 10진수 변환 (long 범위 초과 가능성)
errno = 0;
parsedValue = strtol(decimalNum, &endPtr, 10);
if (errno == ERANGE && (parsedValue == LONG_MAX || parsedValue == LONG_MIN)) {
printf("문자열 '%s' (10진수) -> long: 오버플로우 발생! (값: %ld)\n", decimalNum, parsedValue);
} else if (*endPtr != '\0') {
printf("10진수 변환 실패 (남은 문자: '%s'): %s\n", endPtr, decimalNum);
} else {
printf("문자열 '%s' (10진수) -> long: %ld\n", decimalNum, parsedValue);
}
// 잘못된 형식의 문자열 변환
errno = 0;
parsedValue = strtol(invalidNum, &endPtr, 10);
if (endPtr == invalidNum) { // 변환된 문자가 없는 경우 (즉, 숫자로 시작하지 않음)
printf("문자열 '%s' -> long: 숫자 형식이 아님. (값: %ld)\n", invalidNum, parsedValue); // 0
}
return 0;
}
8.1.5. strtoul 함수
strtoul 함수는 문자열을 unsigned long int 값으로 변환합니다. 원형은 unsigned long strtoul(const char *str, char **endptr, int base); 이며, strtol과 유사하게 작동하지만, 부호 없는 긴 정수를 처리합니다.
#include <stdio.h>
#include <stdlib.h> // strtoul 함수를 위해 필요
#include <errno.h> // errno를 위해 필요
#include <limits.h> // ULONG_MAX를 위해 필요
int main() {
char hexString[] = "ABCDEF_Extra"; // 16진수 ABCDEF
char decString[] = "4294967295"; // 10진수 (ULONG_MAX)
char negativeNum[] = "-123"; // 부호 없는 정수에서는 잘못된 형식
char* endPtrU;
unsigned long parsedULong;
// 16진수 변환
errno = 0;
parsedULong = strtoul(hexString, &endPtrU, 16);
if (errno == ERANGE) {
printf("오버플로우/언더플로우 발생: %s\n", hexString);
} else if (*endPtrU != '\0' && *endPtrU != '_') {
printf("16진수 변환 실패 (남은 문자: '%s'): %s\n", endPtrU, hexString);
} else {
printf("문자열 '%s' (16진수) -> unsigned long: %lu (남은 문자: '%s')\n", hexString, parsedULong, endPtrU); // 11259375
}
// 10진수 변환 (ULONG_MAX)
errno = 0;
parsedULong = strtoul(decString, &endPtrU, 10);
if (errno == ERANGE && parsedULong == ULONG_MAX) {
printf("문자열 '%s' (10진수) -> unsigned long: 오버플로우 발생! (값: %lu)\n", decString, parsedULong);
} else {
printf("문자열 '%s' (10진수) -> unsigned long: %lu\n", decString, parsedULong); // 4294967295 (ULONG_MAX)
}
// 음수 문자열 변환 시도
errno = 0;
parsedULong = strtoul(negativeNum, &endPtrU, 10);
if (endPtrU == negativeNum) { // '-' 문자를 숫자로 파싱하지 못함
printf("문자열 '%s' -> unsigned long: 숫자 형식이 아님 (음수). (값: %lu)\n", negativeNum, parsedULong); // 0
} else {
printf("문자열 '%s' -> unsigned long: %lu\n", negativeNum, parsedULong);
}
return 0;
}
8.1.6. strtod 함수
strtod 함수는 문자열을 double 값으로 변환하는 가장 강력하고 유연한 함수입니다. 원형은 double strtod(const char *str, char **endptr); 이며, strtol과 유사하게 변환이 끝난 문자열의 다음 위치를 endptr을 통해 반환하여 오류 여부와 변환되지 않은 부분을 확인할 수 있습니다.
#include <stdio.h>
#include <stdlib.h> // strtod 함수를 위해 필요
#include <errno.h> // errno를 위해 필요
#include <float.h> // DBL_MAX, DBL_MIN을 위해 필요
int main() {
char doubleStr1[] = "3.14159265_pi_value";
char doubleStr2[] = "1.7976931348623157e+308"; // DBL_MAX 근처
char invalidDouble[] = "NotANumber";
char* endPtrD;
double parsedDouble;
// 정상적인 실수 문자열 변환
errno = 0;
parsedDouble = strtod(doubleStr1, &endPtrD);
if (errno == ERANGE) {
printf("오버플로우/언더플로우 발생: %s\n", doubleStr1);
} else {
printf("문자열 '%s' -> double: %.10lf (남은 문자: '%s')\n", doubleStr1, parsedDouble, endPtrD); // 3.1415926500
}
// 매우 큰 실수 문자열 변환 (오버플로우 가능성)
errno = 0;
parsedDouble = strtod(doubleStr2, &endPtrD);
if (errno == ERANGE && (parsedDouble == DBL_MAX || parsedDouble == -DBL_MAX)) {
printf("문자열 '%s' -> double: 오버플로우 발생! (값: %lf)\n", doubleStr2, parsedDouble);
} else {
printf("문자열 '%s' -> double: %.10lf (남은 문자: '%s')\n", doubleStr2, parsedDouble, endPtrD);
}
// 잘못된 형식의 문자열 변환
errno = 0;
parsedDouble = strtod(invalidDouble, &endPtrD);
if (endPtrD == invalidDouble) { // 변환된 문자가 없는 경우
printf("문자열 '%s' -> double: 숫자 형식이 아님. (값: %lf)\n", invalidDouble, parsedDouble); // 0.000000
}
return 0;
}