명품 C++ 프로그래밍 2장 14번 문제는 아래와 같다.


14. 영문 텍스트를 입력받아 알파벳 히스토그램을 그리는 프로그램을 작성하라. 대문자는 모두 소문자로 집계하며, 텍스트 입력의 끝은 ';' 문자로 한다.

영문 텍스트를 입력하세요. 히스토그램을 그립니다.

텍스트의 끝은 ; 입니다. 10000개까지 가능합니다.

Wise men say, only fools rush in

But I can't help. falling in love with you


Shall I stay? Would it be a sin?

If I can't help. falling in love with you


Like a river flows. surely to the sea

Darling so it goes, some things aren't meant to be ; <- ; 뒤 <Enter> 키

총 알파벳 수 179


a(13)    : *************

b(3)     : ***

c(2)     : **

d(2)     : **

e(17)    : *****************

f(5)     : *****

g(5)     : *****

h(8)     : ********

i(19)    : *******************

j(0)     :

k(1)     : *

l(17)    : *****************

m(3)     : ***

n(14)    : **************

o(14)    : **************

p(2)     : **

q(0)     :

r(6)     : ******

s(14)    : **************

t(14)    : **************

u(6)     : ******

v(3)     : ***

w(5)     : *****

x(0)     :

y(6)     : ******

z(0)     :

계속하려면 아무 키나 누르십시오 . . .


먼저 이런 형식의 문제는 cin.getline() 함수를 사용하여 풀어야 하는 문제이다.


getline 함수를 사용한 후 문장 속 알파벳과 그 외 문자들을 구분하고 알파벳 중 대문자, 소문자를 구분하여야 한다.


문제 풀이의 전 과정도 알고 싶다면 아래 더보기를 누르기 바란다.



그러면 이제 제목에서 언급한 배열 Value를 배열 Index로 사용하는 방법을 말해보고자 한다.

문제 중 알파벳 소문자 입력 갯수를 세는 방법은 물론 많겠지만 나는 배열을 이용해보고자 한다.

내가 배열을 사용하는 이유를 간단하게 설명하자면

만약 a가 나오면 배열[0] 값을 +1을 시켜주고 b가 나오면 배열[1] 값을 +1 시켜주는 메커니즘을 사용할 거다.

int형 변수를 26개 만들어서 넣어도 되는데 굳이 이럴 필요 있나? 이러려고 배운게 배열인데.


만드는 방법은 이러하다.  

int형 배열 alpha를 만들어 알파벳 소문자 갯수인 26만큼의 공간을 만들어 준다.

그리고 해당 배열을 모두 0으로 초기화 해준다.

간단하게 int alpha[26] = {0} 이라고 적으면 배열 선언과 동시에 배열 내 모든 값들이 0으로 초기화된다.


이게 글로만 적어버리면 설명하기도 힘들고 이해하기도 힘드니 소스 먼저 보여주고 설명토록 하겠다.

for (int i = 0; i < len; i++)

{

if (isalpha(text[i]) == 2)

{

alpha[tolower(text[i]) - 97]++;

}

}


len 변수는 orig_text 문자열 길이의 값이다. (현재 orig_text 변수와 text 변수의 문자열 길이는 일치하기 때문에 따로 재지정하진 않았다.)


굵게 처리된 부분이 핵심이다.

다른 부분들은 쉽게 이해 할거라 믿고 핵심 부분만 설명하겠다.

97은 알파벳 소문자 'a'의 ASCII Code이다. ('A' 아니다.)


위에 설명 했다시피 tolower 함수는 정수를 반환시킨다.

그럼 text[0] 값이 a라고 가정하여 풀어서 설명하겠다.

① alpha[tolower(text[0]) - 97] ++;


② alpha[tolower('a') - 97] ++;


③ alpha[97 - 97] ++;


④ alpha[0] ++;

보시다시피 a 값이 나오면 alpha[0] 배열을 1 증가 시키는 메커니즘이 된다.

그럼 z를 넣는다면 어떻게 될까?

① alpha[tolower(text[0]) - 97] ++;


② alpha[tolower('z') - 97] ++;


③ alpha[122 - 97] ++;


④ alpha[25] ++;

딱 배열의 끝인 alpha[25]까지 값이 대입된다.


문제 특성상 ASCII Code를 이용한 배열이기 때문에 순수하게 배열 Value로만 index로 사용하진 않았지만, 이런 식으로 추가적으로 배열 Value을 셈하여 Index로 사용할 수 있다.


글을 쓰다보니 문제 전체적인 해설을 하게 되었는데... 설명해주고 싶은게 많았다보다.

명품 C++ 프로그래밍 2장 8번 문제는 아래와 같다.


8. 한 라인에 ';'으로 5개의 이름을 구분하여 입력받아, 각 이름을 끊어내여 화면에 출력하고 가장 긴 이름을 판별하라.

5 명의 이름을 ';'으로 구분하여 입력하세요

>>Mozart;Elvis Presley;Jim Carry;Schubert;Dominggo;

1 : Mozart

2 : Elvis Presley

3 : Jim Carry

4 : Schubert

5 : Dominggo

가장 긴 이름은 Elvis Presley

계속하려면 아무 키나 누르십시오 . . .


문제를 읽어보면 문자열을 입력 받고 이를 ';'로 구분하여 나누어야 한다. 이를 위해서는 cin.getline() 함수에 대해서 먼저 알아야 한다.


getline() 함수의 원형은 아래와 같다고 한다.

cin.getline(char buf[], int size, char delimitChar )

해석해보자면

buf : 입력받은 문자열을 저장할 배열

size : buf[] 배열의 크기

delimitChar : 문자열 입력 끝을 구분할 문자 이다.

일반적으로 변수 delimitChar가 생략되면 디폴트 값인 '\n'로 지정된다.


위 내용을 참고하여 문제의 식을 만들면 cin.getline(buf, size, ';')가 될 것인데,

이렇게 식을 만들고 그냥 빌드를 돌려버리면 buf에는 Mozart 밖에 들어가지 않는다.

이유라하면 Mozart;Elvis Presley;Jim Carry;Schubert;Dominggo; 을 입력하고 엔터키를 누르면 Mozart 뒤에 있는 ';'가 문자열 입력 끝 구분 문자이기 때문에 뒤에 어느 문자를 입력해도 그냥 무시하고 구분 문자 앞 값인 'Mozart'를 저장한다.


그렇다면 위 문제처럼 어떻게 5 명의 이름을 구분하여 출력시킬 수 있을까?

For문을 사용하면 된다.

For(int i = 0; i < 5; i++)

{

   cin.getline(buf, size, ';');

   cout<< buf << endl;

}


이렇게 적으면 5 명의 이름이 모두 출력된다. 무슨 원리일까?

원리는 이러하다. 쉽게 이해하기 쉬운 예시는 cin 객체이다.


기본적으로 num1, num2의 각각의 변수를 받기 위해서는

cin >> num1;

cin >> num2;


이렇게 적어야 하지만 이를 줄여 사용하여


cin >> num1 >> num2;


이렇게 사용하면 입력받을 때 1(띄우고)2로 입력하면 num1과 num2에 각각 1와 2가 저장되어 있다.

자, 그럼 생각해보자. 이 식의 원리를.

입력한 값 순서대로 공백을 구분 문자로 사용하여 입력한 변수에 대입된다. cin.getline함수도 마찬가지이다.

다만 차이점은 구분 문자를 내가 지정할 수 있다는 것이다.

(공백이 포함된 문자열을 입력할 수 있다는 차이점도 있지만 일단 논외이다.)

이런 식으로 생각하면서 풀이하면 이렇다.


Mozart;Elvis Presley;Jim Carry;Schubert;Dominggo;를 입력하고 엔터를 했을 시

i = 0 일 때

buf에는 첫 구분문자 ';' 앞인 Mozart만 입력되어 이 값만 출력


i = 1 일 때

buf에는 그다음 입력 값인 Elvis Presley만 입력되어 이 값만 출력

.

.

.

i = 4 일 때

buf에는 그 다음 입력 값인 Dominggo만 입력되어 이 값만 출력된다.


이렇게 하면 입력받은 값을 구분 문자로 구분하여 값을 뿌려줄 수 있다.

하지만 착각할 수 있는게 있다.

이러한 방식은 문자열을 말그대로 출력'만' 하는 것이다.

필자가 이렇게 적으면 분명 이렇게 생각하는 사람이 있을 것이다.


??? : 아니 문자들이 따로 저장이 되는게 아니면 어떻게 cout으로 buf를 출력했어요?


물론 값을 저장'은' 한다. 그렇지만 저장하여 출력 후 다음 값을 다시 대입해버려 이전 값들이 사라진다.


입력된 모든 값들을 저장 방법은 변수를 하나 새로 만들어 저장하는 방법 밖에 없다.

방법은 string 배열 변수를 쓰던지, char 배열 변수를 사용하든 마음대로 해라.

두 변수의 사용의 차이에 대해서 간단히 설명하자면 이러하다.

  string [] 변수 : string 변수 자체가 문자열이기 때문에 하나의 인덱스에 문자열이 모두 들어간다.

ex : string[0] = "Mozart", string[1] = "Elvis Presley" ... string[4] ="Dominggo"


 char [] 변수 : char 변수는 배열당 한 문자씩 받게 못 넣기 때문에 최소한 2차원 이상의 배열이 필요하다.

ex : char[0][0] = 'M', char[0][1] ='o', char[0][2] = 'z', char[0][3] = 'a', char[0][4] = 'r', char[0][5] = 't', char[0][6] = '\n'

char[1][0] = 'E', char[1][1] = 'l', char[1][2] = 'v', char [1][3] = 'i', char[1][4] = 's', char[1][5] = ' ' char[1][6] = 'P' char [1][7] = 'r' .....


사용은 기호에 따라 다르게 사용하는 것이고 strlen, strcpy 등 문자열 함수를 이용할 것이라면 char 변수를 사용 하는게 정신적으로 좋을 거다.


글이 길어졌는데 글을 쓴 이유는 그냥 하나다.

내가 문제를 어렵게 풀려는 습관이 있는데 그거 때문에 위의 문제 풀면서 어떻게 입력받은 문자열을 '저장'한 후 구분하여 출력을 할까 생각하며 어렵게 만들어 검색해보았는데, 그냥 인터넷에서는 저장하지 않고 그냥 입력받을 때부터 구분하여 출력해버린다;;;


그래서 결론은 그냥 문제를 끝까지 읽고 쉽게 생각 하자.


+ Recent posts