Unit 45 문자열 자르기
-strtok-
strtok는 특정 문자를 기준으로 문자열을 잘라주는 역할을 한다. 마찬가지로 string.h 헤더에 선언돼 있다.
사용은 strtok(<대상 문자열>, <기준 문자>);로 하면 된다. 자른 문자열을 반환하고 더 이상 자를 문자열이 없으면 NULL을 반환한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[30] = "pwnable system hacking"; // 30 크기의 char 형 배열 선언
char *p = strtok(a, " "); // 공백을 기준으로 문자열 자르고 포인터 반환
while (p != NULL) // 문자열을 더 이상 자르지 못할 때까지
{
printf("%s\n", p); // 자른 문자열 출력
p = strtok(NULL, " "); // 다음 문자열을 잘라서 포인터 반환
}
}
위는 더 이상 자르지 못할 때까지 30 크기의 char 형 배열에 저장된 문자열을 잘라서 출력하는 코드다.
공백을 기준으로 잘려서 출력된 것을 볼 수 있다.
반복문 안의 strtok 함수는 자를 문자열이 들어있는 a 대신 NULL을 첫 번째 인자로 줬는데, 이럴경우 직전 strok 함수에서 처리했던 문자열에서 잘린 문자열만큼 다음 문자로 이동한 뒤 다음 문자열을 잘라준다고 한다.
strtok 함수를 사용할 때는 처음에만 자를 문자열을 넣어주고, 그 다음부터는 NULL을 넣어줘야 한다고 한다.
strtok 함수의 원리는 먼저 위 코드를 예시로 들면 첫 번째 strtok 함수는 a에 저장된 문자열에서 첫 공백이 나오는 "pwnable "에서 " "에 NULL을 넣어줌으로써 자른다. while 문 안의 strtok는 NULL이 있으므로 앞에서 잘린 문자열만큼 다음 문자로 이동한 후 공백문자를 찾아서 NULL로 채워서 system을 자른다. 이 반복을 문자열 끝에 존재하는 NULL을 만날 때까지 반복하는 것이다.
strtok 함수는 원본 문자열에 NULL을 넣어가며 자르기 때문에 원본 문자열의 내용이 바뀌게 된다. 이를 주의해야한다.
char *a = "pwnable system hacking";
마찬가지로 위 같이 일반 포인터에 저장된 문자열은 쓰기를 할 수 없으므로 원본 문자열에 NULL을 삽입하는 strtok 함수는 사용할 수 없다.
char *a = malloc(sizeof(char) * 30);
strcpy(a, "pwnable system hacking");
위 같이 동적으로 메모리를 할당하고 문자열을 복사한다면 포인터에도 strtok를 사용할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[30] = "2021-03-13T21:09:23"; // 30 크기의 char형 배열을 선언
char *p = strtok(a, "-T:"); // -, T, : 기준으로 문자열 자름
while (p != NULL) // 더 이상 자를 수 없을 때까지 반복
{
printf("%s\n", p); // 자른 문자열 출력
ptr = strtok(NULL, "-T:"); // -, T, : 기준으로 문자열 자름
}
return 0;
}
위 코드처럼 strtok로 공백 뿐만 아닌 특정 문자를 기준으로도 자를 수 있다. 특히 기준 문자는 한 번에 여러 개 지정이 가능하다. 위는 2020-03-13T21:09:23을 기준 문자열을 -, T, :로 strtok 함수를 이용해 자르는 코드다.
기준 문자를 기준으로 잘려서 출력되는 것을 볼 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char s1[30] = "pwnable system hacking"; // 30 크기의 char형 배열 선언
char *p[10] = { NULL, }; // 자른 문자열을 담을 배열 선언
char *slice = strtok(s1, " "); // 공백 문자를 기준으로 문자열 자름
int i = 0; // 매 반복마다 1씩 증가할 변수
while (slice != NULL) // 더 이상 자를 수 없을 때까지 반복
{
p[i] = slice; // 문자열을 자른 뒤 반환 된 메모리 주소를 p의 i 인덱스 요소에 저장
i++; // i에 1증가
slice = strtok(NULL, " "); // 다음 문자열을 잘라서 포인터 반환
}
for (int i = 0; i < 10; i++)
{
if (p[i] != NULL) // 문자열 포인터 배열의 요소가 NULL이 아닐 때 p[i]에 저장된 문자열 출력
printf("%s\n", p[i]);
}
return 0;
}
위는 다른 strtok 코드들과 똑같이 자르지만 이 잘랐을 때 반환되는 포인터를 배열에 저장해서 자르는 작업이 끝난 후 한번에 자른 문자열들을 출력한다.
공백 기준으로 잘려서 제대로 출력됨을 알 수 있다.
Unit 46 문자열과 숫자를 서로 변환하기
-atoi-
atoi 함수는 문자열을 정수로 바꿔주는 역할을 한다. ASCII string to integer에서 함수 이름을 따왔고, stdlib.h 헤더에 선언돼 있다.
사용은 atoi(<문자열>);로 하면된다. 성공하면 변환된 정수를 반환하고, 실패한다면 0을 반환한다.
또한 atoi는 문자열이 정수로 되어있어야하고 알파벳 영문자, 특수 문자가 포함되면 해당 문자부터는 반환을 하지 않는다. 또한, 처음부터 숫자가 아니면 0으로 변환된다. 아래는 c언어 코딩도장 Unit 46.1에 나와있는 정수에 영문자, 특수 문자가 섞여있을 때 atoi 함수를 쓰면 어떤 수가 반환되는지의 예다.
283a → 283
283a30 → 283
283! → 283
283!30 → 283
a30 → 0
!30 → 0
abc → 0
!@# → 0
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *a = "123456"; // 문자열 123456이 저장된 포인터 변수 선언
printf("%d", atoi(a)); // "123456"을 정수로 변환하여 출력
}
위는 문자열 "123456"을 atoi 함수로 정수형으로 변환한 뒤 출력하는 코드다.
정수형으로 123456이 잘 출력되는 것을 볼 수 있다.
-strtol-
strtol은 특정 진법으로 표기된 문자열을 정수로 변환하는 함수다. 마찬가지로 stdlib.h 헤더에 선언돼 있다.
사용은 strtol(<문자열>, <끝 포인터>, <진법>);으로 하면 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *a = "0xAAAAA"; // "0xAAAAA"가 저장된 문자열 포인터 선언
printf("%d", strtol(a, NULL, 16));
// strtol 함수로 a에 저장된 16진수 문자열을 정수형으로 변환 후 출력
}
위는 16진수 문자열 0xAAAAA를 strtol 함수로 정수형으로 변환시켜 출력하는 코드다.
0xAAAAA의 10진수 형인 699050이 잘 출력된 것을 볼 수 있다.
위에서는 끝 포인터 인자 부분을 NULL로 줬는데, 이 부분은 여러 진수로 이뤄진 문자열을 정수형으로 변환할 때 사용한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *a = "0xA123 10 0xA 90"; // 여러 진수들이 저장된 문자열 포인터
char *end; // 이전 끝 부분을 저장하는 포인터 변수
printf("%d\n", strtol(a, &end, 16)); // a에 저장된 문자열에서 16진수 문자열을 정수로 변환
printf("%d\n", strtol(end, &end, 10)); // end 위치 부터 10진수 문자열을 정수로 변환
printf("%d\n", strtol(end, &end, 16)); // end 위치 부터 16진수 문자열을 정수로 변환
printf("%d", strtol(end, NULL, 10)); // end 위치 부터 10진수 문자열을 정수로 변환
}
끝 부분에 포인터를 넣어준다면 strtol 함수로 문자열을 정수로 변환한 후 변환한 문자의 끝 부분의 주소를 포인터에 저장해주게 된다. 이를 이용해서 위 코드 같이 a에 저장된 여러 진수의 문자열을 strtol로 잘라준 후 끝 부분의 포인터를 반환하고, 그 반환한 포인터를 시작 지점으로 다음 strtol이 사용함으로써 모두 정수형으로 바꿔줄 수 있는 것이다. 마지막은 더 변환할 문자열이 없으므로 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;
}
위 사진은 위 코드에 대한 예시다. 참고하면 좋을 것 같다.
-atof-
atof 함수는 문자열을 실수로 바꿔주는 역할을 한다. 마찬가지로 stdlib.h에 선언돼 있다.
사용은 atof(<문자열>);로 하고 성공하면 변환된 실수를 반환, 실패하면 0을 반환한다.
atoi 함수와 마찬가지로 문자열은 실수로 되어 있어야하고 알파벳, 특수 문자가 포함되면 해당 문자부터 변환을 하지 않는다. 또한 처음부터 숫자가 아니면 0을 반환한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *a = "3.14"; // 3.14 문자열의 주소가 저장된 char형 포인터 a 선언
printf("%f", atof(a)); // a의 문자열을 실수로 변환 후 출력
}
위는 atof 함수를 이용해 "3.14"를 실수로 바꾼 후 출력하는 코드다.
출력 결과를 보면 제대로 변환된 것을 볼 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *a = "3.e5"; // 3.e5 문자열의 주소가 저장된 char형 포인터 a 선언
printf("%e %f", atof(a), atof(a)); // atof로 실수형으로 변환한 후 지수, 실수로 출력
}
위 코드처럼 3.e5 같은 지수 또한 실수형으로 변환이 가능하다.
%e, %f로 출력했기에 첫 번째는 지수 표기법으로 두 번째는 일반 실수형으로 변환되서 출력됐다.
-strtof-
strtof는 atof 함수와 비슷하게 문자열을 실수로 변환하지만 끝 포인터라는 인자를 하나 더 받아서, 여러 개의 실수로 된 문자열을 실수로 변환할 수 있다.
사용은 strtof(<문자열>, <끝 포인터>);로 하면 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *a = "3.14 3.15 3.16"; // 여러 실수가 저장된 문자열 포인터 a
char *end; // 끝 부분이 저장될 포인터
printf("%f\n", strtof(a, &end)); // 문자열을 실수로 변환 후 끝 부분 주소 포인터에 저장
printf("%f\n", strtof(end, &end)); // 끝 부분을 기준으로 변환 후 끝 부분 주소를 포인터에 저장
printf("%f\n", strtof(end, NULL)); // 끝 부분을 기준으로 변환
}
위에서 다뤘던 코드와 비슷하다. 끝 부분이 저장될 포인터를 선언하고, 이 포인터를 strtof의 두 번째 인자로 줘 끝 주소를 저장하고, 다음 strtof는 이 끝 주소부터 변환을 시작하면 되는 것이다. 마지막에는 더 이상 저장할 필요가 없으므로 NULL을 인자로 줬다.
여러 실수 문자열도 실수로 잘 변환되는 것을 볼 수 있다.
참고로 double형으로 실수를 변환하고 싶을 경우에는 strtod 함수를 사용하면 된다.
-sprintf-
앞서 배운 sprintf 함수를 응용하면 정수를 문자열 형태로 변환할 수 있다.
사용은 sprintf(<문자열>, <정수형 서식 지정자>, <정수>);로 하면 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char a[100]; // 넉넉하게 100 크기의 char형 배열 선언
int b = 1024; // int형 변수 b에 1024를 저장
sprintf(a, "%d", b); // b의 값을 문자열로 변환해서 a에 저장
printf("%s", a); // a의 문자열 출력
}
위는 b에 저장된 1024를 sprintf 함수로 문자열로 변환한 후 a에 저장해, a를 출력하는 코드다.
문자열로 변환된 1024가 제대로 출력된 것을 볼 수 있다.
단순 문자열을 합치고 만드는 줄 알았던 sprintf로 정수형을 문자형으로 변환할 수 있을 줄은 몰랐다.(생각해보니 당연하다.) 다른 진수의 정수나 다른 자료형의 경우 그에 맞는 서식 지정자(%x, %f, %d, %e 등)을 써주면 문자형으로 잘 변환할 수 있다.
Unit 47 회문 판별과 N-gram 만들기
-회문 판별-
파이썬에서 다뤘듯이 회문은 level, 이영이 같이 거꾸로 읽어도 제대로 읽은 것과 같은 단어와 문자를 말한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char word[100]; // 입력된 단어를 저장할 문자열 배열
int len; // 문자열의 길이
int isPalin = 1; // 회문인지 아닌지를 판별할 flag
scanf("%s", word); // 단어 입력
len = strlen(word); // word의 길이를 len 변수에 저장
for (int i = 0; i < len; i++) // len만큼 반복
{
isPalin = word[i] == word[len - i - 1] ? 1 : 0;
//word[i]와 word[len-i-1]이 같다면 isPalin는 1, 아니라면 isPalin에 0 저장
}
printf("%s", isPalin == 1 ? "회문임" : "회문이 아님");
// isPalin이 1이라면 회문임, 아니라면 회문이 아님을 출력
return 0;
}
위는 직접 작성해본 회문 알고리즘이다. 윗부분은 주석에 설명돼 있으니 스킵하고 로직 위주로 설명하겠다. 먼저 strlen으로 입력된 단어의 길이를 구하고 그 길이만큼 for문으로 반복한다.
매 반복마다 삼항 연산자로 word의 i 인덱스와 word의 len-i-1(거꾸로 요소를 구하기 위해)가 같은지 검사하고 같다면 isPalin에 1을, 아니라면 0을 저장한다.
반복이 끝난 후 isPalin이 1이라면 "회문임", 0이라면 "회문이 아님"을 출력한다.
// 0부터 문자열 길이의 절반만큼 반복
for (int i = 0; i < length / 2; i++)
{
// 왼쪽 문자와 오른쪽 문자를 비교하여 문자가 다르면
if (word[i] != word[length - 1 - i])
{
// 회문이 아님
isPalindrome = false;
break;
}
}
회문 알고리즘을 작성한 후 코딩도장에서 작성된 회문 알고리즘을 보니 저렇게 문자열 길이의 절반 만큼 반복하고, 회문이 아닌 순간 바로 반복을 끝내주면 좀 더 효율적인 알고리즘이 되겠다는 생각이 들었다.
-N-gram-
n-gram은 연속되 요소를 추출하는 방법이다. 예전에 파이썬 코딩도장에서 했던 그대로다.
예를들어 Hello라는 단어를 2-gram으로 추출한다면
He
el
ll
lo
이렇게 되는 것이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[30];
int len;
scanf("%s", a); // 문자열 입력
len = strlen(a); // 문자열 길이 확인
for (int i = 0; i < len - 1; i++) // 길이 - 1 만큼 반복
{
printf("%c%c\n", a[i], a[i + 1]); // i와 i + 1번째 인덱스까지 출력
}
return 0;
}
위가 직접 작성해본 2-gram 알고리즘이다. 문자열 변수를 선언해서 입력을 받고 strlen 함수로 길이를 구한 후 길이 -1 (2 그램이기 때문에 범위를 벗어나지 않기 위해 -1해줘야한다.)만큼 반복하며 i 인덱스 요소와 i+1 인덱스 요소를 출력하면 된다.
결과를 보면 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 코드다. strtok를 써서 자르는 식으로 사용했는데, 억지로 배웠던 함수를 넣은 감이 있긴 하다. 아무래도 2-gram 알고리즘은 그냥 strlen 정도만 사용해서 작성하는 편이 훨씬 간단한 것 같다.
char *ptr = strtok(text, " "); // " " 공백 문자를 기준으로 문자열을 자름, 포인터 반환
while (ptr != NULL) // 자른 문자열이 나오지 않을 때까지 반복
{
tokens[count] = ptr; // 문자열을 자른 뒤 메모리 주소를 문자열 포인터 배열에 저장
count++; // 인덱스 증가
ptr = strtok(NULL, " "); // 다음 문자열을 잘라서 포인터를 반환
}
위는 코딩도장에 나와있는 단어 단위 2-gram 알고리즘이다. 확실히 단어 단위는 구현이 어려웟 strtok로 문자열을 자르는 식으로 구현하는게 편한 것 같다.
Unit 45 ~ Unit 47 문제
문자열을 자르는 함수는 strtok다.
답 : d
a : 여러 개 지정 가능 X
c : 원본 문자열을 자른다. X
답 : a, c
.을 기준으로 문자 배열 s1을 자른 뒤 메모리 주소를 ptr에 저장하는 코드는 char *ptr = strok(s1, ".");로 작성하면 된다.
답 : b
공백 단위로 s1에 저장된 문자열을 strtok로 자르면 될 것 같다. 1번 칸에는 우선 반환된 메모리 주소를 담을 포인터를 선언하고 strtok 함수로 공백을 기준으로 Alice's까지 자른다. 2번 칸에는 while문으로 해당 포인터가 NULL이 될 때까지 반복하는 반복문을 작성하고, 3번 칸은 strtok 함수에 첫 번째 인자를 NULL로 줘, 이전에 잘랐던 만큼 이동해서 공백을 찾아서 자르면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char s1[40] = "Alice's Adventures in Wonderland";
char *tok = strtok(s1, " "); // 문자열을 공백 기준으로 자르고 그 주소를 tok에 저장
while (tok != NULL) // tok가 NULL이 아니면 반복
{
printf("%s\n", tok);
tok = strtok(NULL, " ");
// 직전 strok 함수에서 잘린 문자열 만큼 다음 문자로 이동한 뒤
// 다음 문자열을 자르고 메모리 주소 반환
}
return 0;
}
.을 기준으로 문자열을 분리하여 각 줄에 출력하는 것이 목적이니 strtok 함수로 문자열을 자르면 될 것 같다.
위에서 작성했던 로직으로 작성하면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[61]; // 링크가 담길 문자열 배열 선언
scanf("%s", a); // 링크 입력
char *p = strtok(a, "."); // .을 기준으로 문자열 자른 후 메모리 주소 반환
while (p != NULL) // 더 이상 자를 수 없을 때까지
{
printf("%s\n", p); // 자른 문자열 출력
p = strtok(NULL, ".");
// 직전 strtok 함수에서 잘랐던 문자열 만큼 다음 문자열로
// 이동해서 다음 문자열을 자르고 메모리 주소 반환
}
}
앞에서 다뤘던 로직이므로 설명은 생략하겠다.
입력된 문자열에서 the의 개수를 찾는 것이 목적이다. 처음에는 the가 나올 때 마다 잘라서 count를 1 증가시켜주는 식으로 접근했지만 이상한 결과가 나와서, 그냥 공백, 쉼표, 마침표 기준으로 자른 후 자른 문자열이 the인지 확인하는 식으로 풀었다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[1001]; // 문자열이 들어갈 1001 크기의 char형 배열 선언
scanf("%[^\n]s", a); // 공백을 포함해서 문자열 입력 받음
char *p = strtok(a, " ,."); // " ,."를 기준 문자로 문자열을 자름
int count = 0; // the의 수를 count할 변수
while (p != NULL) // 더 이상 자를 문자열이 없을 때까지
{
if (strcmp(p, "the") == 0) // 자른 문자열이 the와 같다면 count+1
{
count++;
}
p = strtok(NULL, " ,."); // " ,."을 기준으로 다음 문자열을 자름
}
printf("%d", count); // count 수 출력
}
입력되는 문자열의 최대 크기가 1000이므로 NULL포함 1001 크기의 문자열 배열을 선언하고, %[^\n]s 서식 지정자를 사용해 공백을 포함해서 문자열을 입력받는다. 그리고 문자열을 공백, 쉼표, 마침표 기준으로 입력 받은 문자열에서 자른다. while 돌리기 전에 count 변수를 선언해주고 while은 더 이상 자를 문자열이 없을 때까지(p가 NULL일 때까지) 반복한다.
매 반복마다 strtok로 자른 문자열이 the와 같은지 strcmp로 검사하고 결과가 0이라면(같다면) count에 1을 더해준다. 그 후 다시 ,.로 문자열을 잘라주고 the와 같은지 검사를 반복한다.
마지막으로 위 반복을 마쳤을 때 the의 개수가 들어있을 count를 printf로 출력하면 된다.
b : 여러 자료형들을 문자열로 합쳐(변환)주는 함수다. X
답 : b
문자열은 16진수로 변환하는 방법은 num1 = strtol(s1 , NULL, 16);으로 하면 된다.
답 : b
c : 16진수이기는 하지만 알파벳이 나오면 atof 함수로는 변환할 수 없다. X
답 : c
정수를 16진법으로 표기 된 문자열로 변환하는 방법은 sprintf(s1, "%x", <정수>);로 해주면 된다.
답 : e
실수를 문자열로 변환할 때 사용하는 서식 지정자는 %f, %e가 있다.
답 : c, g
strtol 함수가 선언된 헤더 파일은 stdlib.h다.
답 : b
문자열 20972를 정수형으로 변환하는 것이 목적이므로 atoi 함수를 사용하면 될 것 같다. 헤더 부분도 비었으니 atoi 함수가 선언된 stdlib.h도 include하면 될 것 같다.
#include <stdio.h>
#include <stdlib.h> // atoi 함수가 선언된 stdlib.h 헤더 incldue
int main()
{
char *s1 = "20972";
int num1;
num1 = atoi(s1); // atoi 함수로 문자열을 정수로 변환
printf("%d\n", num1);
return 0;
}
문자열을 16진수로 변환하는 것이 목적이므로 strtol 함수를 써주면 될 것 같다. 16진수 문자열이 하나이므로 2번째 인자로는 NULL을 주면 된다.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "0x1facefee";
int num1;
num1 = strtol(s1, NULL, 16); // strtol 함수로 16진수 문자열을 정수형으로 변환
printf("0x%X\n", num1);
// 0x<대문자 16진수> 형식으로 출력하기 위해 앞에 0x를 붙여주고 %x대신 %X를 사용
return 0;
}
코드를 보면 s1에 저장된 문자열을 실수로 변환해서 출력하는 것 같으므로 s1에 출력될 97.527824를 문자열 형태로 넣어준 후 2번칸에 s1의 값이 실수 형태로 변환 된 num1을 출력하게끔하면 될 것 같다.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "97.527824"; // 97.527824을 문자열 형태로 저장
float num1;
num1 = atof(s1);
printf("%f\n", num1); // num1을 출력
return 0;
}
여러 개의 실수로 된 문자열을 실수로 변환하는 것이 목적이다. 끝 주소를 인자로 받는 strtof 함수를 사용하면 될 것 같다.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s1 = "29.977213 4528.112305";
float num1;
float num2;
char *end;
num1 = strtof(s1, &end); // s1을 실수로 변환한 후 끝 주소를 end에 저장
num2 = strtof(end, NULL); // 끝 주소를 기준으로 실수로 변환
printf("%f\n", num1);
printf("%f\n", num2);
return 0;
}
float, int형 두 수를 문자열로 만드는 것이 목적이므로 1번 칸에는 만들어진 문자열이 들어갈 문자열 변수 s1을 선언하고 2번 칸에는 sprintf 함수로 두 수를 합쳐주면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char s1[100]; // 넉넉하게 크기 100으로 선언
float num1 = 98.415237f;
int num2 = 0x3fce1920;
sprintf(s1, "%f 0x%x", num1, num2); // num1, num2를 형식에 맞게 서식 지정자를 써줘서 합쳐줌
printf("%s\n", s1);
return 0;
}
주어진 문자열을 각각 16, 10, 실수 형으로 변환해서 출력하는 것이 목적이다. 주어진 칸에 strtol, strtof를 써서 문자열을 각각의 숫자형으로 변환해주면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char s1[31];
int num1;
int num2;
float num3;
char *end;
scanf("%[^\n]", s1);
num1 = strtol(s1, &end, 16); // strtol 함수로 정수로 변환 후 끝 주소를 end에 저장
num2 = strtol(end, &end, 10); // end부터 문자열을 정수로 변환 후 끝 주소를 end에 저장
num3 = strtof(end, NULL); // end부터 문자열을 실수형으로 변환
printf("0x%x\n", num1);
printf("%d\n", num2);
printf("%f\n", num3);
return 0;
}
먼저 num1의 값으로 strtol로 s1의 문자열을 정수로 변환 후 끝 주소를 포인터 end에 저장했고, 그 다음 num2도 strtol로 end 주소를 기점으로 문자열을 정수로 변환한 후 끝 주소를 포인터 end에 저장했다. num3는 실수형으로 변환해야하므로 strtol이 아닌 strtof 함수로 end 주소를 기점으로 문자열을 실수로 변환했다.
입력된 정수, 실수를 문자열로 출력하는 것이 목적이다. s1, s2 문자열 변수에 sprintf 함수로 입력 값을 문자열로 각각 변환해준 후 저장해주면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char s1[10];
char s2[20];
int num1;
float num2;
scanf("%d %f", &num1, &num2);
sprintf(s1, "%d", num1); // num1의 값을 문자열로 변환 후 s1에 저장
sprintf(s2, "%f", num2); // num2의 값을 문자열로 변환 후 s2에 저장
printf("%s\n", s1);
printf("%s\n", s2);
return 0;
}
sprintf 함수를 써줘서 s1, s2에 각각 입력된 정수(num1), 입력 된 실수(num2)를 문자열로 변환해서 저장해줬다.
정수의 회문을 판별하는 알고리즘을 완성시키는 것이 목적이다. 일단 이미 만들어진 알고리즘을 보니 정수로 입력 받은 것을 문자열로 바꿔서 회문을 판별하는 거 같으니 1번 칸에는 sprintf로 text 배열에 문자열 형태로 입력 받은 정수를 변환하면 될 것 같다. 2, 3번은 begin + 1, end - 1을 각각 해줘서 비교가 되게끔 해주면 될 것 같다.
#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);
sprintf(text, "%lld", num1); // 입력받은 정수를 문자열로 변환해 text에 저장
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;
}
begin += 1;
end -= 1;
// begin + 1, end - 1
}
printf("%d\n", isPalindrome);
return 0;
}
입력된 단어를 문자 단위로 4-gram해서 출력하는 것이 목적이다. 1번 칸에는 3글자 이하일 시 wrong을 출력해주는 조건식인 것 같으니 length < 4를 써주면 될 것 같고, 2번 칸은 4-gram이니 i < length - 3을 써주면 될 것 같다. 3번 칸은 4번 반복해야하므로 j < 4를 써주면 될 것 같다.
#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 (length < n)
{
printf("wrong\n");
}
else
{
for (int i = 0; i < length - 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%c", text[i + j]);
printf("\n");
}
}
return 0;
}
공백이 포함된 문자열에서 회문 판별을 하는 것이 목적이다. 이때 회문 판별의 조건으로 공백을 포함하면 안된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {
char a[31]; // 31 크기 문자열을 담을 배열 선언
int len; // 문자열의 길이를 담을 변수
scanf("%[^\n]s", a); // 공백을 포함해서 문자열 입력
len = strlen(a); // 문자열 길이 구하기
int isPalin = 1; // 회문 판별 flag
for (int i = 0, j = len - 1; i < j; i++, j--) { // i, j를 둘다 선언 후 len/2 만큼 반복
while (a[i] == ' ') // a[i]가 공백이면 i + 1
i++;
while (a[j] == ' ') // a[j]가 공백이면 j - 1
j--;
if (a[i] != a[j]) { // a[i] a[j]가 같지 않으면 isPalin을 0으로 만들고 반복 종료
isPalin = 0;
break;
}
}
printf("%d", isPalin); // isPalin 출력
return 0;
}
로직부분만 설명하겠다. for문에서 i, j를 선언하고 i는 0, j는 문자열 길이 -1로 초기화한다. i는 매 반복마다 +1, j는 매 반복마다 -1을 해서 i, j를 이용해 앞과 뒤를 비교하고자 했다. while문으로 만약 i 혹은 j 인덱스가 공백이라면 i는 1 증가, j는 - 1하도록 했다. i, j 인덱스가 공백이 아니라면 i, j 인덱스의 문자를 비교해서 같은지 확인한다. 만약 다르면 회문이 아니므로 isPalin을 0으로 만들고 break한다. 반복이 모두 끝난 후 isPalin의 값을 출력하면 끝이다.
입력된 정수 만큼 입력된 문자열을 N-gram하는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char a[11];
int n, len;
scanf("%d %s", &n, a);
len = strlen(a);
if (n > len) // n보다 길이가 작다면 wrong 출력
printf("wrong");
else
{
for (int i = 0; i < len - (n-1); i++) // len - (n-1) 만큼 반복
{
for (int j = 0; j < n; j++) // n만큼 a[i+j] 요소를 출력
{
printf("%c", a[i + j]);
}
printf("\n"); // 개행
}
}
}
로직 부분만 다루겠다. 먼저 n보다 문자열의 길이가 작다면 wrong을 출력하고, 아니라면 len-(n-1) 만큼 for문으로 반복한다. 그리고 이중 for문으로 n만큼 반복하도록 작성하고 그 안에서 a의 i+j 요소를 출력한다. 그리고 이중 for문이 끝나는 시점에서 개행을 해주면 된다.
'Old (2021.01 ~ 2021.12) > Programming' 카테고리의 다른 글
c언어 코딩도장 Unit 51 ~ Unit 53 (0) | 2021.03.27 |
---|---|
c언어 코딩도장 Unit 48 ~ Unit 50 (0) | 2021.03.13 |
c언어 코딩도장 Unit 42 ~ Unit 44 (0) | 2021.03.13 |
c언어 코딩도장 Unit 39 ~ Unit 41 (0) | 2021.03.13 |
c언어 코딩도장 Unit 36 ~ Unit 38 (0) | 2021.03.06 |