이 페이지 내용 요약
1. 문자열 관련 함수( 문자열 자르기, 문자열 <-> 숫자 변환)
2. 회문, N-gram 구현
Unit 45. 문자열 자르기
45.1 문자를 기준으로 문자열 자르기
strtok 함수로 특정 문자를 기준으로 문자열을 자를 수 있다. string.h 헤더 파일에 선언되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
int main()
{
char s1[30] = "The Littel Prince";
char *ptr = strtok(s1, " ");
while(ptr != NULL)
{
printf("%s\n", ptr);
ptr = strtok(NULL, " ");
}
return 0;
}
|
cs |
strtok함수는 지정된 문자를 기준으로 문자열을 자른 다. 기준 문자는 큰 따옴표로 묶어야 한다. 위의 경우 공백 문자를 넣어 공백을 기준으로 잘랐다. 잘린 문자열을 한 번에 얻을 수 없어서 while 반복문으로 문자열을 계속 자르다가 문자열이 나오지 않으면 반복문을 끝내는 방식을 사용한다.
while 반복문 안의 strtok 함수는 자를 문자열 부분에 NULL을 넣어 주었다. NULL은 직전 strtok 함수에서 처리했던 문자열에서 잘린 문자열 만큼 다음 문자료 이동한 뒤 다음 문자열을 자른다. ptr = strtok(ptr," "); 처럼 잘린 문자열의 포인터를 다시 넣으면 다음 문자로 이동하지 못하고 처음에 나오는 문자열만 자르게 된다. strtok 함수를 사용할 때는 처음에만 자를 문자열을 넣어주고 그 다음부터는 NULL을 넣어줘야 한다.
처음 호출되는 strtok는 공백문자를 찾아서 NULL로 채운 뒤 문자열의 첫 부분인 The를 자른다.
반복문 안의 strtok에 NULL을 넣어주면 앞에서 잘린 문자열 만큼 다음 문자로 이동한 뒤 NULL로 채운뒤 Little를 자른다. 이런 식으로 반복하여 문자열 맨 끝에 있는 NULL문자를 만나면 반복문을 종료한다.
strtok 함수는 문자열을 새로 생성해서 반환하는 것이 아니라 자르는 부분으로 널문자로 채운 뒤 잘린 문자열의 포인터를 반환하는 것이다. 원본 문자열의 내용을 바꾸므로 사용에 주의해야 한다.
45.2 문자열 포인터 자르기
문자열 포인터에 문자열 리터럴이 들어 있어서 읽기 전용인 상태이면 strtok 함수는 사용할 수 없다.
문자열 포인터를 자를때는 동적 메모리를 할당하고, 문자열을 복사하여 이 문제를 해결할 수 있다.
1
2
3
4
5
6
7
8
9
|
char *s1 = malloc(sizeof(char) * 30);
strcpy(s1, "The Little Prince");
char *ptr = strtok(s1, " ");
while (ptr != NULL)
{
printf("%s\n", ptr);
ptr = strtok(NULL, " ");
}
free(s1);
|
cs |
45.3 날짜와 시간값 자르기
strtok함수는 다양한 특수문자와 알파벳 영문자를 기준으로 문자열을 자를 수 있다. 또한 기준 문자는 한 번에 여러개를 지정할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
int main()
{
char s1[30] = "2015-06-10T15:32:19";
char *ptr = strtok(s1, "-T:");
while(ptr != NULL)
{
printf("%s\n", ptr);
ptr = strtok(NULL, "-T:");
}
return 0;
}
|
cs |
-, T, : 기준으로 문자열을 자르므로 기준 문자를 여러 개 넣을 수 있다.
45.4 자른 문자열 보관하기
문자열을 자르는 while 반복문 안에서 모든 처리를 할 수 없는 상황이 있을 수 있기 때문에 자른 문자열을 보관해야 한다. 다음 코드와 같이 문자열 포인터 배열에 자른 문자열을 보관할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
int main()
{
char s1[30] = "The Little Prince";
char *sArr[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)
printf("%s\n", sArr[i]);
}
return 0;
}
|
cs |
while 반복문 안에서는 자른 문자열의 메모리 주소를 배열에 저장하고, 배열의 인덱스를 증가시킨다.
ptr에 저장된 메모리 주소가 바뀌기 전에 다른 곳에 보관하면 자른 문자열을 나중에도 계속 사용할 수 있다. 이후 for 반복문에서 배열을 출력하였다.
*45.5 퀴즈 *
정답은 d 이다.
정답은 a, c이다. 기준 문자열은 여러 개 지정할 수 있으며 잘린 문자열의 포인터를 반환한다.
정답은 b 이다.
45.6 연습문제 : 문자열 자르기
정답은 다음 코드와 같다.
1. char *ptr = strtok(s1. " ");
2. while(ptr != NULL)
3. ptr = strtok(NULL, " ");
45.7 심사문제 : 문자열 자르기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <stdio.h>
#include <string.h>
int main()
{
char s[61];
scanf("%s", s);
char *ptr = strtok(s, ".");
while(ptr != NULL)
{
printf("%s\n", ptr);
ptr = strtok(NULL, ".");
}
return 0;
}
|
cs |
45.8 심사문제 : 특정 단어 개수 세기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <stdio.h>
#include <string.h>
int main()
{
int cnt = 0;
char s[1001];
scanf("%[^\n]s", s);
char *ptr = strtok(s, " .,");
while(ptr != NULL)
{
if(strcmp(ptr, "the") == 0)
{
cnt++;
}
ptr = strtok(NULL, " .,");
}
printf("%d\n", cnt);
return 0;
}
|
cs |
공백, 점, 쉼표를 기준으로 문자열을 잘랐고 strcmp 함수를 이용해 자른 문자열이 the 이면 cnt 변수에 1씩 누적하고, 마지막에 cnt 변수를 출력하였다.
Unit 46. 문자열과 숫자를 서로 변환하기
46.1 문자열을 정수로 변환하기
atoi 함수를 사용하면 10진법으로 표기된 문자열을 정수로 바꿀 수 있다. stdlib.h 헤더파일에 선언되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "283";
int num1;
num1 = atoi(s1);
printf("%d\n", num1);
return 0;
}
|
cs |
atoi 함수에 문자열을 넣으면 정수가 반환된다. 문자열 정수로 되어 있어야 하며 알파벳, 특수문자 등이 포함되면 해당 문자부터는 변환하지 않는다. 또한 처음부터 숫자가 아니면 0으로 반환된다. 다음은 정수에 영문자, 특수문자가 섞여 있을 때 반환 예 이다.
46.2 특정 진법으로 표기된 문자열을 정수로 변환하기
strtol 함수를 사용하여 16진법으로 표기된 문자열을 정수로 바꿀 수 있다. strtol(문자열, 끝포인터, 진법)의 형태로 사용한다. stdlib.h 헤더파일에 선언되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "0xaf10";
int num1;
num1 = strtol(s1, NULL, 16);
printf("%x %d\n", num1, num1);
return 0;
}
|
cs |
변환할 문자열을 저장한 배열을 넣어주고 16을 지정하면 16진법으로 표기된 문자열을 정수로 변환할 수 있다. 10을 지정하면 10진법으로 표기된 문자열을 정수로 변환할 수 있다.
두 번째 인수는 여러개의 정수로 된 문자열을 변환할 때 사용한다. 여러개의 정수로된 문자열을 각각 변환하는 것은 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "0xaf10 42 0x27C 9952";
int num1;
int num2;
int num3;
int num4;
char *end;
num1 = strtol(s1, &end, 16);
num2 = strtol(end, &end, 10);
num3 = strtol(end, &end, 16);
num4 = strtol(end, NULL, 10);
printf("%x\n%d\n%X\n%d\n", num1,num2,num3,num4);
return 0;
}
|
cs |
s1에는 16진법으로 표기된 숫자 2개와 10진법으로 표기된 숫자 2개사 있다. 두 번째 인수로 끝 포인터를 선언하여 메모리 주소를 넣으면 strtol 함수가 실행된 뒤에는 끝 포인터가 " 42 0x27C 9952" 처럼 이전 숫자의 끝 부분부터 시작하게 된다.
두번째 부터는 문자열에 end를 넣어 이전 숫자의 끝 부분부터 변환하면 된다. 이 과정을 그림으로 확인하면 다음과 같다.
46.3 문자열을 실수로 변환하기
atof 함수를 사용하여 문자열을 실수로 바꿀 수 있다. stdlib.h 헤더 파일에 선언되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "35.212345";
float num1;
num1 = atof(s1);
printf("%f\n", num1);
return 0;
}
|
cs |
atof 함수에 문자열을 넣으면 실수가 반환된다. 문자열은 실수로 되어있어야 하며 알파벳 영문자, 특수문자가 포함되면 해당 문자부터는 변환을 하지 않는다. 또한 처음부터 숫자가 아니면 0으로 변환된다.
다음과 같이 알파벳 e를 사용한 지수 표기법으로 된 문자열도 실수로 바꿀 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "3.e5";
float num1;
num1 = atof(s1);
printf("%e %f\n", num1, num1);
return 0;
}
|
cs |
strtof 함수를 사용하여 여러개의 실수로 된 문자열을 실수로 바꿀수 있다. stdlib.h 헤더파일에 선언되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "35.283672 3.e5 9.281772 7.e-5";
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);
printf("%e\n", num2);
printf("%f\n", num3);
printf("%e\n", num4);
return 0;
}
|
cs |
처음에는 s1을 넣어 문자열을 실수로 변환하고, 끝 포인터는 &end처럼 메모리 주소를 넣어 strtof함수가 실행된 후 끝 포인터가 이전 숫자의 끝 부분부터 시작하게 했다. 두 번째 부터는 end를 넣어 이전 숫자의 끝 부분버터 변환한다. double 형 실수로 변환하는 strtod 함수도 있다.
46.4 정수를 문자열로 변환하기
sprintf 함수를 사용하여 정수를 문자열로 변환할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int main()
{
char s1[10];
int num1 = 283;
sprintf(s1, "%d", num1);
printf("%s", s1);
return 0;
}
|
cs |
변환한 문자열을 저장할 배열을 선언하고, sprintf 함수에 서식지정자로 %d를 설정한 뒤 정수를 문자열로 저장하였다. atoi를 반대로 변환하는 itoa 같은 함수도 있지만 C 표준 함수는 아니다.
다음과 같이 16진법으로 표기된 문자열로 변환하려면 서식지정자 %x를 사용하면 된다.
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int main()
{
char s1[10];
int num1 = 283;
sprintf(s1, "0x%x", num1);
printf("%s\n", s1);
return 0;
}
|
cs |
sprintf 함수에서 서식지정자로 %x를 사용하면 16진법으로 표기된 문자열로 변환할 수 있다. 16진수라는 것을 명확하게 나타내기 위해 앞에 0x를 붙여준 것이다. 서식지정자를 %X로 지정하면 16진수 알파벳 부분이 대문자로 저장된다.
46.5 실수를 문자열로 변환하기
실수를 문자열로 변환할 때도 sprintf 함수를 사용한다. 다음과 같이 서식지정자로 %f를 지정하면 된다. 또한 %e를 지정하여 지수 표기법으로 된 문자열로 변환할 수도 있다.
sprintf(s1, "%f", num1);
sprintf(s1, "%e", num1);
변환된 문자열이 길어질 수 있기 때문에 배열의 크기 또는 동적메모리로 할당한 크기를 잘 확인해야 한다.
46.6 퀴즈
정답은 b이다.
정답은 b이다.
정답은 c 이다.
정답은 e 이다.
정답은 c, g 이다.
정답은 b이다.
46.7 연습문제: 문자열을 10진 정수로 변환하기
정답은 다음 코드와 같다.
1. #include <stdlib.h>
2. num1 = atoi(s1);
46.8 연습문제 : 문자열을 16진 정수로 변환하기
정답은 다음 코드와 같다.
1. num1 = strtol(s1, NULL, 16)
2. "0x%X\n"
대문자로 출력하므로 서식지정자 %X를 사용한다.
46.9 연습문제 : 문자열을 실수로 변환하기
정답은 다음과 같다.
1. "97.527824"
2. num1
46.10 연습문제: 여러개의 실수로 된 문자열을 실수로 변환하기
정답은 다음 코드와 같다.
1. num1 = strtof(s1, &end);
2. num2 = strtof(end, NULL);
46.11 연습문제 : 숫자를 문자열로 변환하기
정답은 다음 코드와 같다.
1. char s1[30];
2. sprintf(s1, "%f 0x%x", num1, num2);
46.12 심사문제 : 문자열을 정수와 실수로 변환하기
정답은 다음 코드와 같다.
num1 = strtol(s1, &end, 16);
num2 = strtol(end, &end, 10);
num3 = strtof(end, NULL);
46.3 심사문제 : 정수와 실수를 문자열로 변환하기
정답은 다음 코드와 같다.
sprintf(s1, "%d", num1);
sprintf(s2, "%f", num2);
Unit 47. 회문 판별과 N-gram 만들기
회문은 유전자 염기 서열 분석에서 많이 쓰이고, N-gram은 빅데이터 분석, 검색 엔진에서 많이 쓰인다.
47.1 회문 판별
회문은 level, SOS 등과 같이 순서를 거꾸로 읽어도 제대로 읽은 것과 같은 단어와 문장을 말한다.
회문인지 판별하려면 첫번째 글자와 마지막 글자가 같고, 안쪽으로 한 글자씩 좁혔을 때 글자가 서로 같으면 회문이다.
다음은 문자열을 배열에 넣은뒤 반복문으로 각 글자를 검사한 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int main()
{
char word[30];
int length;
bool isPalindrome = true;
printf("단어를 입력하세요 : ");
scanf("%s", word);
length = strlen(word);
for(int i = 0; i < length / 2; i++)
{
if(word[i] != word[length - 1 -i])
{
isPalindrome = false;
break;
}
}
printf("%d\n", isPalindrome);
return 0;
}
|
cs |
회문일 경우 1이 출력되고, 회문이 아닐 경우 0이 출력된다.
회문 판별에서 가장 중요한 것은 문자열의 길이이다.
문자열 길이의 절반만큼 반복하며 왼쪽 문자와 오른쪽 문자들을 검색한다. 반복문 안에서 왼쪽문자와 오른쪽 문자를 비교하며 문자가 다르면 isPalindrome 에 false를 넣고 반복문을 끝낸다. 문자열의 마지막 문자는 word[length-1] 이므로 인덱스를 i 만큼 빼주면 오른쪽에서 왼쪽으로 진행할 수 있다.
47.2 N-gram 만들기
N-gram은 문자열에서 N개의 연속된 요소를 추출하는 방법이다. Hello의 경우 2-gram으로 추출하면 다음과 같다.
문자열의 처음부터 끝까지 한 글자씩 이동하며 2 글자씩 추출한다.
#include <stdio.h>
#include <string.h>
int main()
{
char text[30] = "Hello";
int length;
length = strlen(text);
for(int i = 0; i < length - 1; i++)
{
printf("%c%c\n", text[i], text[i+1]);
}
return 0;
}
2-gram이므로 문자열의 끝에서 한 글자 앞까지만 반복하며 현재 문자와 그 다음문자 두 글자씩 출력한다.
만약 3-gram이라면 조건식은 i < length -2가 될 것이고, 문자열 끝에서 두 글자 앞까지 반복하면 된다.
단어 단위 N-gram도 있다. 다음은 공백을 기준으로 구분하여 단어단위 2-gram을 출력하는 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <stdio.h>
#include <string.h>
int main()
{
char text[100] = "this is c language";
char *tokens[30] = {NULL, };
int cnt = 0;
char *ptr = strtok(text, " ");
while (ptr != NULL)
{
tokens[cnt] = ptr;
cnt++;
ptr = strtok(NULL, " ");
}
for(int i = 0; i < cnt -1; i++)
{
printf("%s %s\n", tokens[i], tokens[i+1]);
}
return 0;
}
|
cs |
strtok 함수로 문자열을 자른 뒤 각 단어들을 배열에 넣고, 배열의 마지막에서 요소 한 개 앞까지만 반복하면서 현재 문자열과 그 다음 문자열을 출력하면 된다.
47.3 연습문제 : 정수 회문 판별하기
정답은 다음 코드와 같다.
1. sprintf(text, "%lld", num1)
2. begin++;
3. end--;
입력값이 정수 이므로 sprintf 함수로 문자열로 변환하여 회문을 판별한다.
47.4 연습문제 : 4-gram 만들기
정답은 다음 코드와 같다.
1. length < n
2. i < length - (n-1)
3. j < n
문자열을 반복할 때는 배열의 범위를 벗어나지 않도록 i < length(n-1)로 조건식을 사용했다.
47.5 심사문제 : 공백이 포함된 회문 판별
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int main()
{
char str[31];
int length;
bool isP = true;
scanf("%[^\n]s", str);
length = strlen(str);
for(int i = 0, j = length-1; i < j; i++, j--)
{
while(str[i] == ' ')
i++;
while(str[j] == ' ')
j--;
if(str[i] != str[j])
{
isP = false;
break;
}
}
printf("%d\n", isP);
return 0;
}
|
cs |
반복문에서 문자열의 처음부터, 끝에서부터 반복시키며 공백이 있을 경우 한 칸씩 증가, 감소 시켜서 공백을 지우고, 문자열 앞에서 부터 온 문자와 뒤에서 부터 온 문자가 다르면 회문을 거짓으로 하고, 반복문을 끝낸다.
47.6 심사문제 : N-gram 만들기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <stdio.h>
#include <string.h>
int main()
{
char text[11];
int n;
scanf("%d %s", &n, text);
int length = strlen(text);
if(length < n)
{
printf("wrong\n");
}
else
{
for(int i = 0; i < length - (n-1); i++ )
{
for(int j = 0; j < n; j++)
{
printf("%c", text[i+j]);
}
printf("\n");
}
}
return 0;
}
|
cs |
2중 for문의 안쪽 반복문은 입력한 숫자만큼 반복하므로 입력한 숫자만큼의 문자를 출력할 수 있고, 바깥쪽 반복문으로 1씩 증가시키면서 문자열에서 문자를 한 칸씩 앞으로 가도록 했다.
'Project H4C Study Group' 카테고리의 다른 글
[Project H4C][Hack CTF] Basic_BOF #1 (0) | 2021.03.16 |
---|---|
[Project H4C] C언어 코딩도장(12) (0) | 2021.03.12 |
[Project H4C] C언어 코딩도장(10) (0) | 2021.03.10 |
[Project H4C] C언어 코딩도장(9) (0) | 2021.03.10 |
[Project H4C] 우리집에 GDB 있는데… 메모리 보고갈래? (3) Write-up (0) | 2021.03.09 |