코딩일지(2024-01-11)
문자와 문자열 출력
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
//문자 선언과 출력
char ch = 'A';
printf("%c %d\n", ch, ch);
//문자열 선언 방법1
char java[] = { 'J', 'A', 'V', 'A', '\0' };
printf("%s\n", java);
//문자열 선언 방법2
char py[] = "Python";
printf("%s\n", py);
//문자열 선언 방법3
char csharp[5] = "C#";
printf("%s\n", csharp);
//문자 배열에서 문자 출력
printf("%c %c\n", csharp[0], csharp[1]);
return 0;
}
output>
A 65
JAVA
Python
C#
C #
cf. 다양한 문자열 선언 방법
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
char st1[] = { 'H','E','L','L','O',' ', 'W','O','R','L' ,'D','\0' };
printf("st1: %s\n", st1);
char st2[] = "HELLO WORLD";
int i = 0;
while (st2[i] != '\0') {
printf("st2: %c", st2[i]);
i++;
}
return 0;
}
문자열 중간 ‘\0’을 삽입해 문자열 분리
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
char c[] = "C C++ Java";
printf("%s\n", c);
c[5] = '\0';
printf("%s\n%s\n", c, (c + 6));
//문자 배열의 각 원소를 하나하나 출력하는 방법
c[5] = ' '; //널 문자를 빈 문자로 바꾸어 문자열 복원
char* p = c;
while (*p) //(*p != '\0')도 가능
printf("%c", *p++);
printf("\n");
return 0;
}
문자 입출력 함수에 대해서
“내가 아무리 값을 입력해도 실제로 엔터를 쳤을때 프로그램에 값이 이식되는 것이다. 이를 라인버퍼링이라 부른다.”
1.getchar()
2.putchar()
3._getche()
4._getch()
함수 scanf()와 printf(), gets()와 puts()의 문자열 입출력
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <conio.h>
int main() {
char name[20], dept[30]; //char *name, *dept; 실행 오류 발생
int snum;
printf("학번 이름 학과 입력 >>\n");
scanf("%d %s %s", &snum, dept, name);
printf("출력: %d %s %s \n", snum, dept, name);
char line[101]; // char *line으로는 오류발생
printf("한 행에 학번 이름 학과 입력한 후 Enter 누르고 ");
printf("새로운 행에서 [ctrl + z]를 누르세요\n");
while (gets(line))
puts(line);
printf("\n");
return 0;
}
코드리뷰: 예제와 달리 get_s()사용하는 구문은 여기서 사용하지 못했다. 왜냐하면 컴파일러 버전상 해당 기능이 제대로 동작하지 않았기 때문이다. 솔루션이 있기는 하겠으나 현재는 찾지 못한 상황이다.
scanf()대신 get()활용해보기
Q. 이둘의 차이가 뭘까?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <conio.h>
int main() {
char s[100];
//문자배열 s에 표준입력한 한 행을 저장
gets(s);
//문자배열에 저장된 한 행을 출력
char* p = s;
while (*p)
printf("%c", *p++);
printf("\n");
return 0;
}
문자열 함수 strlen()과 memcpy(), strcmp()의 활용
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {
char src[20] = "C Python";
char dst[20];
printf("%s\n", src);
printf("%zu\n", strlen(src));
memcpy(dst, src, strlen(src) + 1);
printf("%s\n", dst);
memcpy(dst, "안녕하세요!", strlen("안녕하세요!") + 1);
printf("%s\n\n", dst);
char* s1 = "C lang";
char* s2 = "C lang";
printf("strcmp(%s, %s) = %d\n", s1, s2, strcmp(s1, s2));
s1 = "C lang";
s2 = "C lang";
printf("strcmp(%s, %s) = %d\n", s1, s2, strcmp(s1, s2));
printf("strcmp(%s, %s) = %d\n", s2, s1, strcmp(s2, s1));
printf("strcmp(%s, %s, %d) = %d\n", s1, s2, 2, strcmp(s1, s2, 2));
return 0;
}
코드리뷰:
(1~6)
-“C Python”을 출력합니다.
-문자열의 길이를 출력합니다.
-src를 dst로 복사합니다.
-복사된 문자열을 출력합니다.
-“안녕하세요!”를 dst에 복사합니다.
-새로운 문자열을 출력합니다.
(7~10)
-두 문자열을 비교합니다.
-동일한 문자열을 다시 비교합니다.
-이번에는 순서를 바꾸어 비교합니다.
-처음 두글자만 비교합니다.
고찰: memcpy()와 strcmp()를 썼다는 것에 주목할만 하다.
memcpy()는 문자열을 복사해올때 쓰고, strcmp()는 두 문자열을 비교할때 사용한다.
중요! 함수 strcp()와 strcat()를 사용시 주의사항
두 함수 모두 첫번째 인자인 dest는 복사 결과나 연산결과가 뒤탈없이? 저장될 수 있도록 충분한 공간을 확보해야 한다. 우리가 주차장에 들어갔는데, 모닝정도만 들어갈수있는 공간에 대형 버스가 들어간다고 해보자.
그러면 다 못들어가고 잘려버릴 것이다. 마찬가지로 이것도 값이 들어갈 수는 있으나, 다 들어가지는 못하는 이슈가 발생한다. 어려운 말로 무결성이 깨진다고 볼수도 있을 것 같다.
또한 문자열 관련 함수에서 단순히 문자열 포인터를 수정이 가능한 문자열의 인자로 사용할 수 없다. 즉 함수 strcpy()와 strcat()에서 첫 인자로 문자열 포인터 변수는 사용할 수 없다.
문자열 복사와 연결함수 strcpy()와 strcat()활용
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Java";
char source[80] = "C is a language.";
printf("%s\n", strcpy(dest, source));
printf("%s\n", strncpy(dest, "C#", 2));
dest[2] = '\0';
printf("%s\n\n", strncpy(dest, "C#", 3));
dest[3] = '\0';
char data[80] = "C";
printf("%s\n", strcat(data, " is "));
printf("%.*s\n", 2, strncat(data, "a java", 2)); // data 배열 크기를 초과하지 않도록 수정
data[16] = '\0'; // 배열 경계 초과 수정
printf("%s\n", strcat(data, "procedural"));
printf("%s\n", strcat(data, " language."));
return 0;
}
문자열에서 지정한 분리자(델리미터)를 사용해 문자열 토큰 분리
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "C and C++\\t languages are best!";
char* delimiter = " !\\t";
// char *next_token;
printf("문자열 \"%s\"을 >>\n", str);
printf("구분자[%s]를 이용하여 토큰을 추출 >>\n", delimiter);
char* ptoken = strtok(str, delimiter);
while (ptoken)
{
printf("%s\n", ptoken);
ptoken = strtok(NULL, delimiter);
}
return 0;
}
다양한 문자열 관련 함수의 이해
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "JAVA 2022 Python C";
printf("strlen(""python""): %zu\n", strlen("python"));
printf("%s, \n", _strlwr(str));
printf("%s\n\n", _strupr(str));
printf("%s, \n", strstr(str, "VA"));
printf("%s\n", strchr(str, 'A'));
return 0;
}
strlen(), _strlwr(), _strupr(), strstr(),strchr()에 대해 알아보았다.
한가지 알게 된 게, strstr()은 문자열을 인자로 받고, strchr()은 문자를 인자로 받는다.
문자열을 역순을 저장하는 함수 reverse() 구현
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void reverse(char str[]);
int main() {
char s[50];
const char* str = "Python C"; // 포인터를 상수 포인터로 수정
memcpy(s, str, strlen(str) + 1);
printf("%s\n", s);
reverse(s);
printf("%s\n", s);
return 0;
}
void reverse(char str[])
{
int j = (int)strlen(str) - 1; // j를 초기화
for (int i = 0; i < j; i++, j--) // 조건식 수정
{
char c = str[i];
str[i] = str[j];
str[j] = c;
}
}
코드리뷰:
const char* str = "Python C";
와 같이 문자열은 상수포인터로 선언해주는게 일반적이라고 하는 것 같다.
memcpy(s, str, strlen(str) + 1);
를 넣지 않으면 콘솔에 출력은 되지만 모두 깨져서 출력되는 이슈가 있다.
이것의 기능이 아직은 잘 와닿지 않는다.
여러 개의 문자열을 선언과 동시에 저장하고 처리하는 방법
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
char* pa[] = { "JAVA", "C#", "C++" };
char ca[][5] = { "JAVA", "C#", "C++" };
printf("%s ", pa[0]); printf("%s ", pa[1]); printf("%s\n", pa[2]);
printf("%s ", ca[0]); printf("%s ", ca[1]); printf("%s\n", ca[2]);
//문자 출력
printf("%c %c %c\n", pa[0][1], pa[1][1], pa[2][1]);
printf("%c %c %c\n", ca[0][1], ca[1][1], ca[2][1]);
return 0;
}
명령행 인자 출력
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(int argc, char* argv[]) {
int i = 0;
printf("실행 명령해 인자(command line arguments) >> \n");
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
결과>
LAB 여러 문자열 처리
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(int argc, char* argv[]) {
char str1[] = "Python";
char str2[] = "Kotlin";
char str3[] = "Tensorflow";
char* pstr[] = { str1, str2, str3 };
//각각의 3개 문자열 출력
printf("%s ", pstr[0]);
printf("%s ", pstr[1]);
printf("%s \n", pstr[2]);
//문자 출력
printf("%c %c %c\n", str1[0], str2[1], str3[2]);
printf("%c %c %c\n", pstr[0][1], pstr[1][1], pstr[2][1]);
return 0;
}
pstr에 배열ㅇㅇ이름 나열하고 데이터타입 문자열 포인터 맞는지? 배열이름은 뭐다 캐릭터 포인트!
만약 저기 str이 아니고 숫자배열이면?
pstr 앞에
int*
이 들어가야지!이 코드가 그대로 주어지고 이를 동적할당방식으로 바꿔서 표현하라는 문제 나옴!
메모리 길이만큼 strcpy로 붙여넣자.
이 배열 이름이 s다.
s[0], s[1], s[2]이렇게 해서 접근하도록 하자
포인터 배열에 대해서
stringOps라는 함수포인터 배열을 사용하여, 포인터로 이루어진 배열을 생성, 각 요소가 포인트로서 함수를 가리키도록 설정함. 첫번째 포인터는 toUpperCase()를, 두번째 포인터는 toLowerCase()를 가리키도록 함.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void toUpperCase(char* str) {
while (*str) {
*str = toupper((unsigned char)*str);
str++;
}
}
void toLowerCase(char* str) {
while (*str) {
*str = tolower((unsigned char)*str);
str++;
}
}
int main(int argc, char* argv[]) {
char text[] = "Hello World!";
void (*stringOps[2])(char*) = { toUpperCase, toLowerCase }; // 올바른 배열 초기화 문법 사용
stringOps[0](text);
printf("Uppercase: %s\n", text);
stringOps[1](text);
printf("Lowercase: %s\n", text);
return 0;
}
위 함수-포인터-배열을 이용해서 좀더 확장시켜보았다.
※함수 포인터 배열 응용
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void foo(char* str) {
int length = strlen(str); //format: strlen({포인터 이름})
for (int i = 0, j = length - 1; i < j; i++, j--) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
void bar(char* str) {
//1. 한번더 뒤집기(복원하기)
int length = strlen(str); //format: strlen({포인터 이름})
for (int i = 0, j = length - 1; i < j; i++, j--) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
//2. 대문자로 바꾸기
while (*str) { //*str이 가져온 값이 마지막값인 널이 되면 탈출함
*str = toupper((unsigned char)*str);
str++;
}
}
void whatFuck(char* str) {
//1. 소문자로 바꾸기
while (*str) { //*str이 가져온 값이 마지막값인 널이 되면 탈출함
*str = tolower((unsigned char)*str);
str++;
}
}
int main() {
char text[] = "Hello World!";
void(*funcCollection[])(char*) = {foo, bar, whatFuck};
//문자열을 반전시키고 출력
funcCollection[0](text); //<-foo(text);
printf("%s\n", text);
//문자열을 대문자로 바꾸고 출력
funcCollection[1](text);
printf("%s\n", text);
//문자열을 소문자로 바꾸고 출력
funcCollection[2](text);
printf("%s\n", text);
return 0;
}
output>
!dlroW olleH HELLO WORLD! hello world!
void형 포인터에 대해
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
// 주어진 데이터 타입에 따라 값을 출력하는 함수
void printValue(void* value, char type) {
switch (type) {
case 'i':
printf("%d\n", *(int*)value); // 정수형 데이터 출력
break;
case 'f':
printf("%f\n", *(float*)value); // 부동소수점형 데이터 출력
break;
case 'c':
printf("%c\n", *(char*)value); // 문자형 데이터 출력
break;
}
}
int main() {
int i = 10;
float f = 23.5;
char c = 'x';
// 각 데이터 타입에 대한 값을 출력
printValue(&i, 'i');
printValue(&f, 'f');
printValue(&c, 'c');
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
: visual studio에서 발생할 수 있는 경고 메시지를 무시하도록 하는 pragma입니다.printValue
: void형 포인터를 사용하여 임의의 데이터 유형을 받아들이고 ‘type’매개변수를 통해 어떤 유형의 데이터인지 지정을 받아 출력하게 됩니다.main
함수에서는 정수형, 부동소수점형 , 문자형의 변수를 만들어서 ‘printValue’함수를 통해 각각의 값을 출력하고 있습니다.
실습3번문제 응용)
3-2)사용자로부터 아이디와 비밀번호를 입력받아 유효성 검증을 수행하는 프로그램을 작성하세요. 다음과 같은 조건을 따라야 합니다.
-아이디와 비밀번호는 모두 5자 이상 15자 이하이어야 합니다.
-아이디와 비밀번호는 동일해서는 안됩니다.
-비밀번호에는 최소한 한 개 이상의 숫자가 포함되어야 합니다.
-프로그램은 유효한 아이디와 비밀번호가 입력될 때까지 계속해서 입력을 받아야 합니다.
-유효한 아이디와 비밀번호가 입력되면 “Registration Complete”를 출력하고 프로그램을 종료하세요.
정답>
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//비밀번호 유효성 검증 함수
int validatePassword(char* username, char* password) {
//info값 길이 검증
if (strlen(username) < 5 || strlen(username) > 15) {
printf("Retry\n");
return 0; // 비밀번호 길이가 유효하지 않음
}
if (strlen(password) < 5 || strlen(password) > 15) {
printf("Retry\n");
return 0; // 비밀번호 길이가 유효하지 않음
}
// 두 info값이 같은지 확인 (같으면 strcmp()에서 0 반환)
if (strcmp(username, password) == 0) {
printf("Retry\n");
return 0; //두 info값이 같음
}
// password에 숫자가 하나도 없는지 확인
printf("password에 숫자가 하나도 없는지 확인");
int flag = 0;
while (*password) {
printf("*password: %c\n", *password);
if (isdigit(*password)) {
flag = 1; //숫자가 하나도 없음
break;
}
password++;
}
if (flag) {
return 1;
}
return 0; //info값 유효성 통과
}
int main() {
char username[20]; //비밀번호를 저장할 배열, 충분한 크기로 지정
char password[20];
do {
//사용자로부터 비밀번호 입력받기
printf("Username: ");
scanf("%s", username);
//두번째 비밀번호 입력받기
printf("password: ");
scanf("%s", password);
} while (!validatePassword(username, password));
printf("Registration Complete\n");
return 0;
}
댓글남기기