본문 바로가기

Programming/C

[ProjectH4C] 코딩도장 Unit 46~47 Write-Up

문자열 변환

프로그래밍을 하다 보면 출력 결과는 숫자이지만 출력 형태는 문자열인 경우가 종종 있다. 이러한 경우에 문자열을 int, float 형으로 변환하거나 int, float형의 숫자를 문자열로 바꾸어주는 함수가 있다.

 

atoi(문자열)

 

atoi() 함수는 문자열을 정수로 바꾸어 준다. Ascii TO Integer의 약자이며, stdlib.h 헤더 파일에 선언되어 있다. 

 

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

 

실행 결과

283

 

문자열에 넣어준 "283"을 atoi 함수를 통해 정수형 변수 num1에 숫자로 저장하는 코드이다. 문자열이 정수로 이루어져 있을 때만 가능하며, 문자열이 숫자로 시작하지 않으면 0으로 변환되고, 숫자로 시작하고 중간에 문자가 들어가 있을 경우, 문자 직전까지의 숫자만 정수형 변수에 저장된다.

 

strtol(문자열, 끝 포인터, 진법)

 

10진수가 아닌 특정한 진법으로 표기된 문자열을 정수로 바꾸는 함수이다. strtol() 함수의 3번째 인자인 진법 부분에 몇진법으로 표기되었는지 넘겨주면, 10진수로 계산되어 변수에 저장된다. 두 번째 인자로 넘기는 끝 포인터는, 하나의 정수로 바꾸는 경우엔 NULL을 주로 넘기지만, 여러 개의 정수를 다양한 진법으로 저장하고자 할 때 이전 정수의 끝 부분을 저장하기 위해 주로 사용한다.

 

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

정수 4개가 문자열 포인터에 저장되어 있다. 이 때, num1에 첫 번째 정수 0xaf10를 정수로 변환한 후 정수가 끝나는 지점을 end 포인터에 저장해 준다. 이 때 end에는 "42 0x27C 9952" 가 저장된다. 다음 번 숫자를 정수형 변수에 저장하기 위해서는 end부터 저장하면 되며, 이 때의 끝 포인터를 end에 저장하게 되면 10진수 42가 num2에 저장되고 end에는 "0x27C 9952"가 저장되게 된다. 이처럼 여러개의 정수를 각각의 변수에 변화해서 저장하고자 할 땐 끝 포인터를 저장할 포인터 변수를 하나 선언해서 이용한다.

 

atof(문자열)

 

문자열을 실수로 변환해준다. atoi() 함수와 마찬가지로 문자로 시작하는 문자열은 0.00000으로 변환되며 중간에 문자를 만나면 문자를 만나기 전까지만 변환되어 저장된다.

 

 

strtof(문자열, 끝 포인터)

 

여러 개의 실수 형태의 문자열을 실수로 바꾸어준다. 사용 방법은 strtol() 함수와 동일하지만, 진법을 따로 표시할 필요없이 모든 수를 10진수로 받아들인다.

 

 

sprintf(문자열, "서식", 정수)

 

해당 함수는 정수를 문자열 형태로 변환하는 함수이다. 먼저 문자열을 저장할 배열을 선언한 후, 해당 함수에 서식지정자 %d 또는 %x를 이용해서 정수를 배열에 문자열의 형태로 저장할 수 있다. 만약 16진수를 저장할 때 10진수와 헷갈리지 않게 "0xAAA"의 형태로 저장하고자 한다면 서식에 "%x"가 아닌 "0x%x"를 작성하면 된다.

 

sprintf(문자열, "서식", 실수)

 

실수를 문자열로 변환할 때도 정수를 변환할 때와 동일한 방법을 사용한다.

 

 

회문(Palindrome)

회문이란, 거꾸로 읽었을 때랑 제대로 읽었을 때가 같은 단어 혹은 문장을 뜻한다. C언어를 이용해서 입력 받은 단어가 회문인지를 판별하는 코드는 다음과 같다.

 

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

 

해당 코드는 입력한 단어(문장)가 회문이면 1을, 회문이 아니면 0을 출력하는 코드이다. 문자열의 길이의 절반을 기준으로 양 끝에서 부터 짝을 지어서 두 문자가 같은 지 확인하기 때문에, i가 문자열 길이의 절반을 넘어간다면 for문을 벗어나도록 조건을 작성하면 된다.

 

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

 

실행 결과

He
el
ll
lo

 

위의 코드는 두 글자를 출력하고 옆으로 이동하고 또 두 글자를 출력하고 이동하고를 반복한다. 두 글자 단위이기 때문에 2-gram 방법이다. 이 때, 반복횟수는 문자열 길이보다 1 적은 경우까지 해야한다. 마지막에서 두 번째 글자부터 두 글자를 출력하면 마지막 글자까지 출력되고 그 것이 마지막이기 때문이다. 

N-gram은 실제로 어떠한 문장에서 특정 단어를 추출하여 빈도를 세는 데에 활용되어서 검색엔진, 빅데이터 등에서 주로 활용되는 방식이다.

 

 

 

 

 

 

 

 


#46.6 퀴즈

sprintf() 함수는 정수, 실수를 문자열로 변환하는 함수이다. 답은 b.

 

 

 

 

정수로 변환하기 때문에 정수형 변수에 저장해야 하고 16진수 이기 때문에 strtol() 함수의 세 번째 인자로 16을 넘겨야 한다. 답은 b.

 

문자열을 실수로 변환할 때 소수점 아래 6자리 소수 혹은 지수표기법으로 변환할 수 있다. 0x1.103은 잘못된 형식이므로 답은 c.

16진수의 서식 지정자는 %x이다. 답은 e.

 

실수를 문자열로 변환할 때에는 %f 혹은 지수표기법인 %e이다. 답은 c, g.

 

문자열과 실수, 정수 간의 변환에 관련된 함수는 stdlib.h 헤더 파일에 선언되어 있다. 답은 b.

 

 

#46.7 연습문제: 문자열을 10진 정수로 변환하기

#include <stdio.h>
①___________________

int main()
{
    char *s1 = "20972";
    int num1;

    ②___________________

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

    return 0;
}

실행 결과

20972

문자열을 정수로 변환해야 하기 때문에 atoi() 함수를 사용한다. atoi() 함수는 stdlib.h 헤더 파일에 선언 되어 있기 때문에 stdlib.h 헤더 파일을 포함해주어야 한다.

1. #include <stdlib.h>

2. num1 = atoi(s1);

 

 

#46.8 연습문제: 문자열을 16진 정수로 변환하기

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *s1 = "0x1facefee";
    int num1;

    ①______________________________

    printf(②_________, num1);

    return 0;
}

 

실행 결과

0x1FACEFEE

 

16진수로 변환해서 num1에 저장해야 한다. strtol() 함수를 사용하면 된다. 출력 할 때에도 "0x%X" 형태로 출력해주어야 실행 결과와 동일하게 출력된다.

1. num1 = strtol(s1, NULL, 16)

2. "0x%X"

 

#46.9 연습문제: 문자열을 실수로 변환하기

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *s1 = ①___________;
    float num1;

    num1 = atof(s1);

    printf("%f\n", ②__________);

    return 0;
}

 

실행 결과

97.527824

 

문자열을 실수로 변환하는 함수는 atof() 함수이다. 

1. "97.527824"

2. num1

 

 

#연습문제 46.10: 여러 개의 실수로 된 문자열을 실수로 변환하기

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *s1 = "29.977213 4528.112305";
    float num1;
    float num2;
    char *end;

    ①_____________________
    ②_____________________

    printf("%f\n", num1);
    printf("%f\n", num2);

    return 0;
}

 

실행 결과

29.977213 
4528.112305

여러 개의 실수를 저장할 때 사용하는 함수는 strtof() 함수이다. 첫 번째 변환할 땐 s1문자열을 넘겨주고, 두 번째 이후부터는 end를 넘겨주어야 한다.

1. num1 = strtof(s1, &end);

2. num2 = strtof(end, NULL);

 

 

#46.11 연습문제: 숫자를 문자열로 변환하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    ①_______________
    float num1 = 98.415237f;
    int num2 = 0x3fce1920;

    ②_____________________________

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

    return 0;
}

 

실행 결과

98.415237 0x3fce1920

 

문자열에 수를 저장할 땐 sprintf() 함수를 사용하면 된다. 문자형 배열(포인터)을 먼저 만들어 준 후에 변환해서 넣어주면 된다. 

1. char s1[50];

2. sprintf(s1, "%f 0x%x", num1, num2);

 

 

#46.12 심사문제: 문자열을 정수와 실수로 변환하기

 

하나의 문자열로 입력받은 각각의 수를 num1, num2, num3에 저장해야 한다. num1, num2는 strtol() 함수를 이용해 변환하고 num3은 strtof() 함수를 이용해 변환해서 저장한다. 이 때 각각의 수가 변환되면 다음 수를 저장할 수 있도록 끝 포인터 end를 이용해야 한다.

 

num1 = strtol(s1, &end, 16);
num2 = strtol(end, &end, 10);
num3 = strtof(end, NULL);

 

 

 

#46.13 심사문제: 정수와 실수를 문자열로 변환하기

두 수 num1, num2를 각각 s1, s2 문자형 배열에 저장해야 한다. sprintf() 함수를 이용해 변환하면 된다.

 

sprintf(s1, "%d", num1);
sprintf(s2, "%f", num2);

 

 

 

#47.3 연습문제: 정수 회문 판별하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int main()
{
    long long num1;
    char text[30];

    printf("정수를 입력하세요: ");
    scanf("%lld", &num1);

    ①______________________________

    int length;
    bool isPalindrome = true;

    length = strlen(text);

    int begin = 0;
    int end = length - 1;
    while (begin < end)
    {
        if (text[begin] != text[end])
        {
            isPalindrome = false;
            break;
        }

        ②_____________
        ③_____________
    }

    printf("%d\n", isPalindrome);

    return 0;
}

회문인지 판별하는 코드를 완성해야 한다. 먼저, 입력받은 정수를 문자열에 저장해야한다.  그 후에 입력한 문자열의 길이를 length 변수에 저장하고, begin, end를 각각 시작점과 끝점으로 초기화 해주고 while문을 통해 text[begin]과 text[end]가 같은지를 비교한다. 다른 경우 break를 이용해 반복문을 벗어나는 코드가 구현되어 있기 때문에 그 아래에는 반복을 계속하기 위한 코드를 작성해야 한다. 두 문자가 같으면 그 다음번의 문자를 비교해야 하기 때문에 begin을 1 증가, end를 1 감소 시켜주어야 한다. 이 때 입력한 정수가 long long의 범위이기 때문에 sprintf() 함수를 이용해서 변환 후 저장할 때 서식 지정자 "%lld"를 이용한다.

1. sprintf(text, "%lld", num1);

2.begin++;

3.end--;

 

 

#47.4 연습문제 4-gram 만들기

 

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

int main()
{
    char text[30];
    int length;
    int n = 4;

    scanf("%s", text);

    length = strlen(text);
    if (①________________)
    {
        printf("wrong\n");
    }
    else
    {
        for (int i = 0; ②_____________; i++)
        {
            for (int j = 0; ③___________; j++)
                printf("%c", text[i + j]);

            printf("\n");
        }
    }

    return 0;
}

실행 결과

overwhelming (입력)
over
verw
erwh
rwhe
whel
helm
elmi
lmin
ming

문제에서 3글자 이하일 때 wrong을 출력하라 했기에 1번은 3보다 큰지에 대한 조건식을 작성하면 된다. 그 후에 i를 통해 문자열의 길이 -3까지(마지막 4글자를 출력하고 종료되도록) 동작하도록 조건식을 작성하고, j를 통해 4번 반복하면서 해당 위치부터 4글자를 출력하도록 조건식을 작성하면 된다.

 

1. length<4

2. i<length-3

3. j<n

 

 

#47.5 심사문제: 공백이 포함된 회문 판별

 

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

int main(){
    char s1[31];
    int length;
    int isPalindrome=1;
    
    scanf("%[^\n]s", s1);
    length = strlen(s1);
    
    for(int i=0, j=length-1; i<j; i++, j--){
        while(s1[i]==' ')
            i++;
        while(s1[j]==' ')
            j--;
        if(i>j || s1[i]!=s1[j]){
            isPalindrome = 0;
            break;
        }
    }
    
    printf("%d \n", isPalindrome);
    
    return 0;
}

for문을 이용해 i,j 를 각각 0, strlen(s1)-1로 초기화 해주어서 시작점과 끝점을 가리킬 수 있도록 한 후에, i를 1씩 증가시키고 j를 1씩 감소시키며 두 문자를 비교한다. 이 때, s1[i] 혹은 s1[j]가 공백일 수도 있기 때문에 둘이 같은지 확인하기 전에 공백일 경우 공백이 아닐 때까지 증가, 감소 시켜주어서 공백이 아닌 문자를 가리킬 수 있도록 반복문을 작성해 주었다. 공백이 아닐때까지 반복문을 돌리면 i가 j보다 커질 수 있기 때문에 if문의 조건에 i>j 를 추가해주면 된다.

 

 

#47.6 심사문제: N-gram 만들기

if문을 통해 입력된 길이가 더 작으면 wrong을 출력하고 그렇지 않다면 n-gram을 출력한다. 먼저, 외부의 반복문은 시작점을 정하기 위해 i를 0부터 length-(number-1)까지 반복하고, 내부의 반복문은 j=0부터 j<number까지 반복 출력할 횟수를 정해준다. 그 후에 출력할 땐, s1[i+j]로 시작점부터 j가 반복되는 만큼의 길이의 글자를 출력한다.

 

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

int main(){
    int number;
    int length;
    char s1[11];
    scanf("%d %s", &number, s1);
    length = strlen(s1);
    if(length < number){
        printf("wrong \n");
    }
    else{
        for(int i=0; i<length-(number-1); i++){
            for(int j=0; j<number; j++ ){
                printf("%c", s1[i+j]);
            }
            printf("\n");
        }
    }
    
    return 0;
}