C 언어 문자열 처리 함수

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;
}

태그: C language string manipulation Standard Library String functions Character arrays

9월 28일 12:45에 게시됨