티스토리 뷰

 

본 강좌는 아래 동영상 강좌와 같이 진행됩니다. 되도록이면 동영상과 같이 보시는 것을 추천합니다.

 

유튜브 채널 가기

 

강좌 11편 동영상 보기

 


  

 이번시간에는 자료형과 변수를 알아볼때 나왔었던 '문자열'과 그 처리를 알아보도록 하겠습니다.

 

 

 

 1. 문자와 문자열

 

 지난번 자료형, 변수를 알아볼때 나왔던 내용을 잠시 살펴보겠습니다.

 

문자 : char, 1 바이트, 서식문자 %c

문자열 : char[], []안의 숫자만큼의 바이트, 서식문자 %s

 

 선언 및 초기화는 이런식으로 했었습니다.

 

문자

char abc = 'a';

 

문자열

char abc[10] = "abcdefg";

 

 문자는 char형으로 선언하며, 초기화 및 대입할때 작은 따음표 ''로 묶어야 합니다. 단일 문자이기 때문에 한글자만 들어갈 수 있고, 그렇기 때문에 1바이트의 크기를 가집니다.

 

 문자열은 char형인것은 문자와 같지만, 선언할때 변수명 뒤에 [10]과 같이 최대 크기를 지정하고, 최대 이 숫자만큼의 문자를 넣을 수 있습니다. 초기화할때는 넣어줄 문자열을 큰따음표 ""로 묶어줍니다.

 

 사실상 문자열은 문자를 []안의 갯수만큼 길게 붙인것에 불과하고, 별도로 문자열형 변수 자체가 C언어에는 없습니다. 그렇기 때문에 최대로 들어갈 수 있는 문자수는 이 갯수까지입니다. 그런데 다음을 보면

 

char abc[10] = "abcde12345";

 

printf("%s", abc);

 

 이 코드를 실행해보면

  


 다음과 같이 abcde12345 뒤에 뭔가 이상한 값이 같이 출력됩니다. 뒤 한개의 문자를 줄여 abcde1234만 넣어 출력해보면

 


 잘 출력이 되는걸 확인할 수 있습니다.

 

 위 예에서 문자열 변수 abc의 크기는 최대 10까지이고, 문자 몇개로 문자열이 이루어져 실제 값이 들어가있는지를 컴파일러는 알지 못합니다. 그렇기 때문에 문자열의 끝은 '널문자 (Null charactor)'라는 것으로 판단하게 됩니다. 첫번째 실행에서 출력할때 이상한 값이 뒤에 붙어 출력된것은 최대 크기 10의 문자열 변수에 10개의 문자를 넣었기 때문에 널문자를 만나지 못해서 그 범위를 벗어나 출력이 된 것입니다.

 

 C언어에서의 문자열을 정리하자면, '널 문자로 끝나는 단일문자의 연결'이라고 할 수 있겠습니다. 문자열을 가지고 어떤 작업을 하든, 반드시 알아야 할 사항이므로 꼭 기억을 해 둡시다.

 

 널문자는 아스키코드 0번입니다. 숫자 0과 구분해주기 위해 '\0'으로 사용합니다. 변수에 아무것도 없는것으로 초기화 한다면

 

char abc = '\0';

char abc[10] = "\0";

 

 이와 같이 해주면 됩니다.

 

 

 

 2. 문자열 처리

 

 문자열 변수로 한가지 실험을 해봅시다.

 

char abc[10] = "\0";

abc = "abcde";

printf("%s", abc);

 

 이렇게 하고 컴파일을 해보면 두번째 줄에서 에러가 발생을 합니다.

 


 에러메시지를 따로 설명하진 않겠습니다. 문자열 변수를 초기화할때는 ""로 묶은 문자열을 직접 대입하는 것이 가능하지만, 이후 코드 중간에 다른 문자열 값을 '=' 연산자를 통해 직접 대입할 수 없습니다. 이유는 '배열'을 다룰때 설명하기로 하고, 문자열 변수의 값을 중간에 바꾸는 방법부터 알아보도록 하겠습니다.

 

 그 전에 코드에 다음과 같이 해더파일을 포함 시켜줍니다.

 

#include <string.h>

 

 이 'string.h' 안에는 C에서 제공하는 문자열 처리에 관한 다양한 내용이 들어있기 때문에 아래의 내용을 사용하려면 반드시 포함시켜줘야 합니다.

 

 ◆ 문자열 복사 (strcpy(), strncpy())

 위에서 알아본 바와 같이 문자열을 직접 대입할 수 없기 때문에, 중간에 문자열을 담기 위해서는 문자열 복사 함수 strcpy()를 이용합니다.

 

strcpy(문자열을 담을 변수명, 담길 문자열 값);

 

 만약 abc라는 변수에 "홍길동" 이라는 문자열을 넣고자 한다면

 

char abc[10] = "\0";

strcpy(abc, "홍길동");

 

 이와 같이 사용합니다.

 

 그런데 비주얼 스튜디오에서 해보면 아래 '출력'창에 다음과 같은 경고가 표시됩니다.

 


 전에 입력문을 알아볼때 scanf()를 사용하면 저 비슷한 경고가 나왔었는데, '문자열을 담을 변수'의 크기가 '담을 문자열'보다 작을 경우 문제가 생기기 때문에 strcpy_s()를 사용하라는 이야기입니다. strcpy_s()로 사용하려면

 

strcpy_s(문자열을 담을 변수명, 변수의 크기, 담을 문자열 값);

 

 이와 같이 사용합니다. 위의 예를 고쳐보면

 

char abc[10] = "\0";

strcpy_s(abc, 10, "홍길동");

 

 이렇게 표현할 수 있습니다.

 

 그리고 이런식으로 변수끼리도 사용이 가능합니다.

 

char a[10] = "홍길동";

char b[10] = "\0";

 

strcpy(a, b);

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

 

실행 결과

 

홍길동

 

 문자열의 전체를 복사할때는 strcpy()를 사용하지만, 일부분을 복사할때는 다음과 같이 strncpy()를 사용합니다.

 

strncpy(문자열을 담을 변수명, 담을 문자열 값, 담을 크기);

 

 마지막 부분의 '담을 크기'는 문자열의 맨 앞에서부터 몇번째까지 담을 것인지를 넣어주면 됩니다. 예를 들어보면

 

char a[10] = "\0";

strncpy(a, "abcdefg", 3);

 

실행 결과

 

abc

 

 저 크기는 정확하게는 복사할 문자의 갯수가 아니라 복사할 바이트수입니다. 이 바이트수는 유니코드나 기타 문자코드에 따라 달라지지만 우리는 지금 저것을 사용하고 있지 않기 때문에 한 문자당 1바이트로 계산해서 3이라는 값을 넣은 것입니다.  한글의 경우는 한글자당 2바이트이므로 한글을 사용하려면 이것을 유의해야 합니다.

 

 ◆ 문자열 길이 구하기 (strlen())

 문자열 처리를 하다보면 가장 빈번하게 문자열의 길이를 구하는 경우가 생깁니다.

 

문자열의 길이 = strlen(문자열);

 

 예를 하나 들어봅시다.

 

int a = 0;

char abc[10] = "abcde";

 

a = strlen(abc);

printf("문자열의 길이는 %d입니다.\n", a);

 

실행 결과

 

문자열의 길이는 5입니다.

 

  위의 예에서처럼 개행문자를 제외한 문자열의 길이 5를 반환합니다. 역시 이것도 문자의 갯수가 아니라 문자열의 바이트수입니다. 한글의 경우는 한글자당 2바이트이기 때문에 값이 달라질 것입니다.

 

 또한 strlen()은 문자열 끝의 개행 문자까지 세지 않습니다. 그렇기 때문에 위의 예에서 문자열의 길이가 5로 나와있어도 실제로는 개행문자 포함 6개만큼인 것이라는걸 꼭 기억하시기 바랍니ㅏㄷ.

 

 ◆ 문자열 비교 (strcmp(), stricmp())

 두개의 문자열을 비교할때는 다음과 같이

 

비교 결과 = strcmp(문자열1, 문자열2);

 

 와 같이 사용합니다. 비교 결과는 정수형으로, '문자열1'이 크면 음수, 같으면 0, 문자열2가 크면 양수값을 반환합니다. 예를 들어

 

char a1[10] = "abc";

char a2[10] = "abd";

int chk = 0;

 

chk = strcmp(a1, a2);

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

 

실행 결과

 

-1

 

  이와 같이 음수인 '-1'이 출력됩니다. strcmp()는 문자열을 구성하는 각각의 문자 (정확히는 바이트)를 아스키코드값으로 변환하여 차례대로 비교하는 것입니다. 그렇기 때문에 대문자 'A'와 소문자 'a'는 서로 같지 않습니다.  이런 대소문자 구분을 하지 않고 문자열을 비교하려면 아래 예처럼

 

char a1[10] = "abcde";

char a2[10] = "aBcdE";

 

if (stricmp(a1, a2) == 0)

printf("두 문자열이 같습니다.\n");

else

printf("두 문자열이 다릅니다.\n");

 

실행 결과

 

두 문자열이 같습니다.

 

 이렇게 strcmp() 대신에 stricmp()를 사용하면 대소문자 구분을 하지 않고 비교합니다. 이러한 문자열 비교는 문자열들을 정렬하거나 문자열 값이 어떤 값과 같은지 비교하여 처리할때 많이 사용합니다.

 

 문자열 관련 함수는 이밖에도 상당히 많고, 그중에는 자주 사용하는 것들도 많습니다. 하지만 그것들을 설명하고 이해하는데에는 필수적으로 '배열'과 '포인터'에 대한 이해가 필요하기 때문에, 그 이후에 한번더 나머지 문자열 함수에 대해 알아보는 것으로 하겠습니다.

 

 

 

 3. 프로그램에 적용

 

  우리가 만들고 있는 '성적관리 프로그램'에는 몇가지 문제점이 있는데, 그 중에서 몇가지를 수정해봅시다.

 

다음처럼 이름을 입력해보면

 


 다음과 같이 제대로 입력처리가 되지 않고, 여기서 2번을 눌러 출력해보면

 


 전혀 엉뚱한 값이 들어가 있는것을 볼 수 있습니다. 이러한 현상은 이름을 담는 변수 'name'의 크기가 20이기 때문에 그보다 큰 문자열을 입력하면 발생하게 됩니다.

 

 입력 부분 코드를 다음과 같이 수정해봅시다.

 

#include <stdio.h>

#include <string.h>

 

void main()

{

...

...

char temp[256] = "\0";  // 입력값 임시저장

int len = 0;                     // 입력값 길이

 

do

{

...

...

 

switch (menuChoice)

{

case '1':

{

do

{

printf("이름을 입력하세요 : ");

scanf_s("%s", temp, sizeof(temp));

len = strlen(temp) + 1;

 

if (len > sizeof(name))

printf("너무 긴 이름을 입력하셨습니다.\n");

else

strcpy_s(name, sizeof(name), temp);

} while (len > sizeof(name));

 

...

...

 

}

break;

}

 

...

...

 

 기존 코드에서는 scanf()로 name 변수에 직접 값을 저장했던것을, 비교적 큰 공간을 가지도록 선언한 변수 temp에 우선 저장되고, 그 길이를 구해 name에 들어갈 수 있는지를 판단한 다음, 크다면 메시지를 출력하고 do while문에 의해 다시 입력을 받습니다. name에 들어갈 수 있는 길이라면 temp에 저정된 문자열을 name으로 복사합니다.

 

 그런데 아래 부분에서

 

len = strlen(temp) + 1;

 

 temp의 길이를 구해 1을 더한다음 len 변수에 저장합니다. 여기서 1을 더하는 이유는 strlen()의 값은 개행문자를 포함하지 않기 때문입니다. 만약 여기서 1을 더하지 않는다면 20글자를 입력받았을때 name에 복사가 되고, 개행문자가 없기 때문에 이름 뒤로 이상한 문자가 찍힐 것입니다.

 

 아래는 오늘의 최종 소스코드입니다. 이번부터는 초기화를 모두 0으로 해주었습니다.

 

#include <stdio.h> 

#include <string.h> 

 

void main()

{

char name[20] = "\0";   // 이름

char grade = '\0';                 // 등급

int scoreKOR = 0;              // 국어점수

int scoreMAT = 0;              // 수학점수

int scoreENG = 0;              // 영어점수

int scoreSCI = 0;               // 과학점수

char comment[200] = "\0";        // 평가

 

int scoreTotal = 0;            // 총점

float scoreAverage = 0.0F;       // 평균

 

char menuChoice = '\0';        // 메뉴 선택

 

char temp[256] = "\0";        // 입력값 임시저장

int len = 0;                           // 입력값 길이

 

do

{

// 메뉴 출력

printf("*** 성적관리 프로그램 ***\n");

printf("\n");

printf("1. 학생 성적 입력\n");

printf("2. 성적 확인\n");

printf("\n");

printf("Q. 프로그램 종료\n");

printf("\n");

printf("작업할 번호를 입력하세요 : ");

 

// 메뉴 선택

menuChoice = getchar();

 

switch (menuChoice)

{

case '1':

{

 // 사용자 입력

do

{

printf("이름을 입력하세요 : ");

scanf_s("%s", temp, sizeof(temp));

len = strlen(temp) + 1;

 

if (len > sizeof(name))

printf("너무 긴 이름을 입력하셨습니다.\n");

else

strcpy_s(name, sizeof(name), temp);

} while (len > sizeof(name));

 

printf("국어점수를 입력하세요 : ");

scanf_s("%d", &scoreKOR);

printf("수학점수를 입력하세요 : ");

scanf_s("%d", &scoreMAT);

printf("영어점수를 입력하세요 : ");

scanf_s("%d", &scoreENG);

printf("과학점수를 입력하세요 : ");

scanf_s("%d", &scoreSCI);

 

getchar();

printf("평가를 입력하세요 : ");

gets_s(comment, sizeof(comment));

}

break;

 

case '2':

{

// 총점 계산

scoreTotal = scoreKOR + scoreMAT + scoreENG + scoreSCI;

// 평균 계산

scoreAverage = (float) scoreTotal / 4;

 

// 평균값에 대응하는 등급 구하기

if (scoreAverage >= 90)

grade = 'A';

else if (scoreAverage >= 80)

grade = 'B';

else if (scoreAverage >= 70)

grade = 'C';

else if (scoreAverage >= 60)

grade = 'D';

else

grade = 'F';

 

// 화면 출력

printf("%s의 성적\n", name);

printf("등급 : %c\n", grade);

printf("국어 점수 : %d\n", scoreKOR);

printf("수학 점수 : %d\n", scoreMAT);

printf("영어 점수 : %d\n", scoreENG);

printf("과학 점수 : %d\n", scoreSCI);

printf("총점 : %d\n평균 : %.1f\n", scoreTotal, scoreAverage);

printf("평가 : %s\n", comment);

 

getchar();

}

break;

 

case 'Q':

case 'q':

break;

 

default:

printf("잘못 입력하셨습니다.\n\n");

getchar();

}

} while (menuChoice != 'Q' && menuChoice != 'q');

}

 

 이번시간에는 문자열과 그 처리방법에 대해 알아보았습니다. 다음시간에는 '배열'에 대해 알아보도록 하겠습니다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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 29 30 31