profile image

L o a d i n g . . .

Unit 45. 문자열 자르기

45.1 문자를 기준으로 문자열 자르기

strtok 함수는 특정 문자를 기준으로 문자열을 자르는 방법이다. (string.h 헤더 파일에 선언되어 있음).

  • strtok(대상문자열, 기준문자);
#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strtok 함수가 선언된 헤더 파일

int main()
{
    char s1[30] = "The Little Prince";  // 크기가 30인 char형 배열을 선언하고 문자열 할당

    char *ptr = strtok(s1, " ");      // " " 공백 문자를 기준으로 문자열을 자름, 포인터 반환

    while (ptr != NULL)               // 자른 문자열이 나오지 않을 때까지 반복
    {
        printf("%s\n", ptr);          // 자른 문자열 출력
        ptr = strtok(NULL, " ");      // 다음 문자열을 잘라서 포인터를 반환
    }

    return 0;
}

strtok 함수는 지정된 문자를 기준으로 문자열을 자른다. 즉, strtok(s1, " ");와 같이 " " (공백 문자)를 넣어주면 공백으로 구분하여 문자열을 자른다(단, 기준 문자를 ' '로 묶으면 안 됨).

 

strtok 함수의 동작 순서는 다음과 같다.

1. 먼저 처음 호출되는 strtok " " (공백 문자)를 찾아서 NULL로 채운 뒤 문자열의 첫 부분인 "The"를 자른다.

 

2. 그 다음 while 반복문 안의 strtok NULL을 넣어주어 앞에서 잘린 문자열만큼 다음 문자로 이동한다. 그리고 다시 공백 문자를 찾아서 NULL로 채운 뒤 "Little"을 자른다. 하지만 아직 문자열 끝에 있는 NULL을 만나지 못했으므로 계속 반복한다.

 

3. 다시 strtok NULL을 넣어주어 앞에서 잘린 문자열만큼 다음 문자로 이동한다. 이번에는 공백 문자가 아닌 문자열 마지막의 NULL을 만났으므로 NULL을 그대로 두고 "Prince"를 반환한다.

 

※ strtok 함수는 문자열을 새로 생성해서 반환하는 것이 아니라 자르는 부분을 널 문자(NULL)로 채운 뒤 잘린 문자열의 포인터를 반환합니다. 따라서 원본 문자열의 내용을 바꾸므로 사용에 주의해야 한다.

 

 

45.2 문자열 포인터 자르기

문자열 포인터에 문자열 리터럴이 들어있어서 읽기 전용인 상태라면 strtok 함수를 사용할 수 없다.

char *s1 = malloc(sizeof(char) * 30);    // char 30개 크기만큼 동적 메모리 할당

strcpy(s1, "The Little Prince");    // s1에 문자열 복사

char *ptr = strtok(s1, " ");    // 동적 메모리에 들어있는 문자열은 자를 수 있음

while (ptr != NULL)
{
   printf("%s\n", ptr);
   ptr = strtok(NULL, " ");
}

free(s1);    // 동적 메모리 해제

문자열 포인터에 문자열 리터럴을 할당하는 대신 동적 메모리를 할당하고, 문자열을 복사하면 이 문제를 해결할 수 있다.

 

 

45.3 날짜와 시간값 자르기

strtok 함수는 공백 문자뿐만 아니라 다양한 특수 문자와 알파벳 영문자를 기준으로 문자열을 자를 수 있다.

특히 기준 문자는 한 번에 여러 개를 지정할 수 있다.

#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strtok 함수가 선언된 헤더 파일

int main()
{
    char s1[30] = "2015-06-10T15:32:19";    // 크기가 30인 char형 배열을 선언하고 문자열 할당

    char *ptr = strtok(s1, "-T:");    // -, T, 콜론을 기준으로 문자열을 자름
                                      // 포인터 반환

    while (ptr != NULL)               // 자른 문자열이 나오지 않을 때까지 반복
    {
        printf("%s\n", ptr);          // 자른 문자열 출력
        ptr = strtok(NULL, "-T:");    // 다음 문자열을 잘라서 포인터를 반환
    }

    return 0;
}

위 예제는 연-월-일T시:분:초 형식으로 된 문자열 "2015-06-10T15:32:19"을 잘라서 "2015", "06", "10", "15", "32", "19"를 각 줄에 출력한다.

-, T, : (콜론)을 기준으로 문자열을 자르므로 "-T:"와 같이 기준 문자를 여러 개 넣었다.

 

 

45.4 자른 문자열 보관하기

이번에는 문자열 포인터 배열에 자른 문자열을 보관해보겠다.

#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strtok 함수가 선언된 헤더 파일

int main()
{
    char s1[30] = "The Little Prince";    // 크기가 30인 char형 배열을 선언하고 문자열 할당
    char *sArr[10] = { NULL, };    // 크기가 10인 문자열 포인터 배열을 선언하고 NULL로 초기화
    int i = 0;                     // 문자열 포인터 배열의 인덱스로 사용할 변수

    char *ptr = strtok(s1, " ");   // 공백 문자열을 기준으로 문자열을 자름

    while (ptr != NULL)            // 자른 문자열이 나오지 않을 때까지 반복
    {
        sArr[i] = ptr;             // 문자열을 자른 뒤 메모리 주소를 문자열 포인터 배열에 저장
        i++;                       // 인덱스 증가

        ptr = strtok(NULL, " ");   // 다음 문자열을 잘라서 포인터를 반환
    }

    for (int i = 0; i < 10; i++)
    {
        if (sArr[i] != NULL)           // 문자열 포인터 배열의 요소가 NULL이 아닐 때만
            printf("%s\n", sArr[i]);   // 문자열 포인터 배열에 인덱스로 접근하여 각 문자열 출력
    }

    return 0;
}

먼저 char *sArr[10] = { NULL, };와 같이 자른 문자열을 보관할 문자열 포인터 배열을 선언하고 NULL로 초기화했다. 여기서 NULL 뒤에 , (콤마)를 붙여주면 배열의 모든 요소가 NULL로 초기화된다.

while 반복문 안에서는 sArr[i] = ptr;과 같이 자른 문자열의 메모리 주소를 배열에 저장하고, 배열의 인덱스를 증가시킨다.

 

 

 

Unit 46. 문자열과 숫자를 서로 변환하기

46.1 문자열을 정수로 변환하기

  • atoi(문자열);
#include <stdio.h>
#include <stdlib.h>    // atoi 함수가 선언된 헤더 파일

int main()
{
    char *s1 = "283";   // "283"은 문자열
    int num1;

    num1 = atoi(s1);        // 문자열을 정수로 변환하여 num1에 할당

    printf("%d\n", num1);   // 283

    return 0;
}

단, 문자열은 정수로 되어있어야 하며 알파벳 영문자, 특수 문자가 포함되면 해당 문자부터는 변환을 하지 않는다. 또한, 처음부터 숫자가 아니면 0으로 변환된다.

 

 

46.2 특정 진법으로 표기된 문자열을 정수로 변환하기

  • strtol(문자열, 끝포인터, 진법);
#include <stdio.h>
#include <stdlib.h>    // strtol 함수가 선언된 헤더 파일

int main()
{
    char *s1 = "0xaf10";    // "0xaf10"은 문자열
    int num1;

    num1 = strtol(s1, NULL, 16);    // 16진법으로 표기된 문자열을 정수로 변환

    printf("%x %d\n", num1, num1);  // af10 44816

    return 0;
}

num1 = strtol(s1, NULL, 16);처럼 변환할 문자열을 저장한 배열(또는 동적 메모리를 할당한 포인터)을 넣어주고 16을 지정하면 16진법으로 표기된 문자열을 정수로 변환할 수 있다. 

 

 

#include <stdio.h>
#include <stdlib.h> // strtol 함수가 선언된 헤더 파일

int main()
{
    char *s1 = "0xaf10 42 0x27C 9952"; // "0xaf10 42 0x27C 9952"는 문자열
    int num1;
    int num2;
    int num3;
    int num4;
    char *end;    // 이전 숫자의 끝 부분을 저장하기 위한 포인터

    num1 = strtol(s1, &end, 16);     // 16진법으로 표기된 문자열을 정수로 변환
    num2 = strtol(end, &end, 10);    // 10진법으로 표기된 문자열을 정수로 변환
    num3 = strtol(end, &end, 16);    // 16진법으로 표기된 문자열을 정수로 변환
    num4 = strtol(end, NULL, 10);    // 10진법으로 표기된 문자열을 정수로 변환

    printf("%x\n", num1);    // af10
    printf("%d\n", num2);    // 42
    printf("%X\n", num3);    // 27C
    printf("%d\n", num4);    // 9952

    return 0;
}

다음은 여러 개의 정수로 된 문자열을 변환하는 법이다. 먼저 s1에는 "0xaf10 42 0x27C 9952"와 같이 16진법으로 표기된 숫자 2개, 10진법으로 표기된 숫자 2개씩 정수가 4개 들어있다. 그리고 처음에는 num1 = strtol(s1, &end, 16);와 같이 s1의 첫 부분을 정수로 변환한다. 여기서 끝 포인터는 &end처럼 end의 메모리 주소를 넣어준다. 

 

그래서 두 번째부터는 num2 = strtol(end, &end, 10);와 같이 end를 넣어주어 이전 숫자의 끝 부분부터 변환하면 된다.더 변환할 문자열이 없다면 num4 = strtol(end, NULL, 10);와 같이 NULL을 넣어주면 된다.

 

 

46.3 문자열을 실수로 변환하기

  • atof(문자열);
#include <stdio.h>
#include <stdlib.h>    // atof 함수가 선언된 헤더 파일

int main()
{
    char *s1 = "35.283672";  // "35.283672"은 문자열
    float num1;

    num1 = atof(s1);         // 문자열을 실수로 변환하여 num1에 할당

    printf("%f\n", num1);    // 35.283672

    return 0;
}

단, 문자열은 실수로 되어있어야 하며 알파벳 영문자, 특수 문자가 포함되면 해당 문자부터는 변환을 하지 않는다.

 

 

  • strtof(문자열, 끝포인터);
#include <stdio.h>
#include <stdlib.h>    // strtof 함수가 선언된 헤더 파일

int main()
{
    char *s1 = "35.283672 3.e5 9.281772 7.e-5";    // "35.283672 3.e5f 9.2817721 7.e-5f"는 문자열
    float num1;
    float num2;
    float num3;
    float num4;
    char *end;    // 이전 숫자의 끝 부분을 저장하기 위한 포인터

    num1 = strtof(s1, &end);     // 문자열을 실수로 변환
    num2 = strtof(end, &end);    // 문자열을 실수로 변환
    num3 = strtof(end, &end);    // 문자열을 실수로 변환
    num4 = strtof(end, NULL);    // 문자열을 실수로 변환

    printf("%f\n", num1);    // 35.283672
    printf("%e\n", num2);    // 3.000000e+05
    printf("%f\n", num3);    // 9.281772
    printf("%e\n", num4);    // 7.000000e-05

    return 0;
}

사용법은 위의 strtol과 유사하다.

 

 

46.4 정수를 문자열로 변환하기

  • sprintf(문자열, "%d", 정수);
  • sprintf(문자열, "%x", 정수);
  • sprintf(문자열, "%X", 정수);
#define _CRT_SECURE_NO_WARNINGS    // sprintf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>     // sprintf 함수가 선언된 헤더 파일

int main()
{
    char s1[10];       // 변환한 문자열을 저장할 배열
    int num1 = 283;    // 283은 정수

    sprintf(s1, "%d", num1);    // %d를 지정하여 정수를 문자열로 저장

    printf("%s\n", s1);         // 283

    return 0;
}

먼저 변환한 문자열을 저장할 배열을 선언한다(동적 메모리를 할당해도 됨). 그리고 sprintf 함수에 서식 지정자로 %d를 설정한 뒤 정수를 문자열로 저장한다.

 

 

46.5 실수를 문자열로 변환하기

  • sprintf(문자열, "%f", 실수);
#define _CRT_SECURE_NO_WARNINGS    // sprintf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>     // sprintf 함수가 선언된 헤더 파일

int main()
{
    char s1[10];               // 변환한 문자열을 저장할 배열
    float num1 = 38.972340f;   // 38.972340은 실수

    sprintf(s1, "%f", num1);   // %f를 지정하여 실수를 문자열로 저장

    printf("%s\n", s1);        // 38.972340

    return 0;
}

sprintf함수에 서식 지정자로 %f를 지정하면 실수를 문자열로 변환할 수 있다. 

 

 

 

Unit 45. 회문 판별과 N-gram 만들기

47.1 회문 판별

회문(palindrome)은 순서를 거꾸로 읽어도 제대로 읽은 것과 같은 단어와 문장을 말한다.

회문을 잘 살펴보면 첫 번째 글자와 마지막 글자가 같다. 그리고 안쪽으로 한 글자씩 좁혔을 때 글자가 서로 같으면 회문이다.

#define _CRT_SECURE_NO_WARNINGS    // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int main()
{
    char word[30];               // 단어를 저장할 배열
    int length;                  // 문자열 길이
    bool isPalindrome = true;    // 회문 판별값을 저장할 변수, 초깃값은 true

    printf("단어를 입력하세요: ");
    scanf("%s", word);

    length = strlen(word);    // 문자열의 길이를 구함
    
    // 0부터 문자열 길이의 절반만큼 반복
    for (int i = 0; i < length / 2; i++)
    {
        // 왼쪽 문자와 오른쪽 문자를 비교하여 문자가 다르면
        if (word[i] != word[length - 1 - i])
        {
            // 회문이 아님
            isPalindrome = false;
            break;
        }
    }

    printf("%d\n", isPalindrome);    // 회문 판별값 출력

    return 0;
}

위 소스 코드를 보면 문자열 길이의 절반만큼만 반복하면서 왼쪽 문자와 오른쪽 문자들을 검사한다. 

반복문 안에서 왼쪽 문자 word[i]와 오른쪽 문자 word[length - 1 - i]를 비교하여 문자가 다르면 회문이 아니므로 isPalindrome false를 넣어주고 반복문을 끝낸다. 어차피 회문이 아니기 때문에 더 검사할 필요가 없다.

 

 

47.2 N-gram 만들기

N-gram은 문자열에서 N개의 연속된 요소를 추출하는 방법이다.

#include <stdio.h>
#include <string.h>

int main()
{
    char text[30] = "Hello";
    int length;

    length = strlen(text);    // 문자열의 길이를 구함

    // 2-gram이므로 문자열의 끝에서 한글자 앞까지만 반복함
    for (int i = 0; i < length - 1; i++)
    {
        printf("%c%c\n", text[i], text[i + 1]);    // 현재 문자와 그다음 문자 출력
    }

    return 0;
}

다음은 문자 단위 2-gram을 출력해본 것이다. 실행 결과는 다음과 같다.

He
el
ll
lo

2-gram이므로 문자열의 끝에서 한 글자 앞까지만 반복하면서 현재 문자와 바로 그다음 문자 두 글자씩 출력한다.

 

 

#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>

int main()
{
    char text[100] = "this is c language";
    char *tokens[30] = { NULL, };    // 자른 문자열의 포인터를 보관할 배열, NULL로 초기화
    int count = 0; // 자른 문자열 개수

    char *ptr = strtok(text, " ");   // " " 공백 문자를 기준으로 문자열을 자름, 포인터 반환

    while (ptr != NULL)            // 자른 문자열이 나오지 않을 때까지 반복
    {
        tokens[count] = ptr;       // 문자열을 자른 뒤 메모리 주소를 문자열 포인터 배열에 저장
        count++;                   // 인덱스 증가

        ptr = strtok(NULL, " ");   // 다음 문자열을 잘라서 포인터를 반환
    }

    // 2-gram이므로 배열의 마지막에서 요소 한 개 앞까지만 반복함
    for (int i = 0; i < count - 1; i++)
    {
        printf("%s %s\n", tokens[i], tokens[i + 1]);    // 현재 문자열과 그다음 문자열 출력
    }

    return 0;
}

다음은 문자열을 공백으로 구분하여 단어 단위 2-gram을 출력한다. 예를 들어 "this is c language" "this is", "is c", "c language"가 된다.

먼저 strtok 함수로 text를 자른 뒤 각 단어들을 tokens 배열에 넣는다. 그리고 2-gram이므로 tokens 배열의 마지막에서 요소 한 개 앞까지만 반복하면서 현재 문자열과 그다음 문자열을 출력하면 된다.

복사했습니다!