[C언어 강좌] #16-2 문자열 표준 함수와 기타 표준 함수


sscanf() 함수와 sprintf() 함수

우리가 지금까지 C언어에서 필수적으로 사용해온 함수로 scanf() 함수와 printf() 함수가 있습니다.

이번에 알아볼 함수는 scanf()와, printf() 함수 이름 앞에 s를 붙인 sscanf() 함수와 sprintf() 함수 입니다.

 

함수 원형 설명
#include <string.h>
int sscanf(const char * s1, const char * s2, ...)
메모리(문자열)에서 데이터를 입력받음
성공 : 데이터의 개수 반환
#include <string.h>
int sprintf(const char * s1, const char * s2, ...)
메모리(문자열)에 데이터를 출력(저장)
성공 : 문자열의 길이 반환

*위 함수의 인자중에 ...은 함수의 인자가 많이 있을 수 있는 가변 인자를 나타냅니다.

이후 동적 메모리 할당과 가변 인자편에서 자세히 배울 예정입니다.

 

우리가 주로 많이 사용하던 scanf() 함수는 키보드로부터 데이터를 입력받는 함수,

printf() 함수는 모니터에 데이터를 출력하는 함수입니다.

 

이제부터 배울 sscanf() 함수는 메모리부터 데이터를 입력받는 함수이고,

sprintf() 함수는 메모리에 데이터를 출력하는 함수입니다.

 

입력과 출력의 대상이 바뀌었을뿐 큰 차이는 없습니다.

 

sscanf() 함수 사용하기

먼저 sscanf() 함수의 예제를 가지고 알아보겠습니다.

#include <stdio.h>
#include <string.h>


int main(){
	char array[50] = "100 3.14 good-morning";
	int num1;
	double num2;
	char str[50];
	
	//scanf("%d %lf %s", &num1, &num2, str); 키보드로부터 입력받음 
	sscanf(array, "%d %lf %s", &num1, &num2, str); //배열로부터 입력받음
	
	puts("Output");
	printf("%d %lf %s", num1, num2, str); 
	return 0;
}

Output
100 3.140000 good-morning
--------------------------------
Process exited after 0.004636 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

sscanf()의 사용방법이 좀 보이시나요?

 

기본적으로 우리가 지금까지 사용해온 scanf 함수는 이런식으로 사용했습니다.

키보드로부터 형식에 맞게 데이터를 입력받고, 그것을 변수에 각각 저장합니다.

 

그런데 sscanf()의 경우 키보드로부터 데이터를 입력받는게 아닌 문자열으로 부터 데이터를 형식대로 입력받아서 변수에 각각 저장합니다. 

 

문자열 배열의 경우 메모리에 저장되어 있을것이므로 메모리부터 데이터를 입력받는다는 의미가 문자열로부터 데이터를 입력받는다는 의미로 이해하시면 됩니다.

 

#include <stdio.h>
#include <string.h>


int main(){
	char array[50] = "5000000";
	int num;
	
	sscanf(array, "%d", &num); //배열로부터 입력받음
	puts("Output");
	printf("%d", num);
	
	return 0;
}

Output
5000000
--------------------------------
Process exited after 0.004215 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

문자열로부터 입력을 받는 sscanf() 함수의 경우 매우 유용하게 사용할 수 있는데 한가지 예가 바로 위 예제입니다.

 

보시면 문자열로 구성된 "5000000" 이라는 문자를 sscanf()를 통해 입력받고 서식문자 %d로 num에 저장함으로써 숫자5000000으로 바꿔버릴 수 있습니다.

 

문자열을 서식에 맞춰서 다른 변수에 저장할 수 있는 함수가 바로 sscanf() 입니다.

 

 

sprintf() 함수 사용하기

sprintf() 함수도 sscanf() 함수와 마찬가지로 매우 유용하게 사용 가능한 함수입니다.

 

마찬가지로 예제를 보고 익혀보겠습니다.

 

#include <stdio.h>
#include <string.h>


int main(){
	char array[50];
	int num1 = 100;
	double num2 = 3.14;
	char str[50] = "good-morning";
	
	printf("%d, %lf, %s \n", num1 , num2, str); //모니터에 출력 
	sprintf(array, "%d, %lf, %s \n", num1, num2, str); //배열에 출력 
	puts(array);
	return 0;
}

100, 3.140000, good-morning
100, 3.140000, good-morning


--------------------------------
Process exited after 0.004456 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

기존에 출력을 위해서 printf() 함수를 사용할때는,

printf("%d, %lf, %s \n", num1 , num2, str); 이런식으로 사용을 했습니다.

각각 변수들이 서식 문자에 맞춰서 콘솔창(모니터)에 출력되었습니다.

 

sprintf() 함수의 경우에는 일반 printf 함수와 다르게 맨앞에 문자열을 입력받는데, 

sprintf(array, "%d, %lf, %s \n", num1, num2, str); 이렇게 사용하면

각각 변수들이 서식 문자에 맞춰서 문자열(배열 array)에 출력합니다.

 

문자열에 출력한다는건 문자열에 저장한다는 의미와 같습니다. 출력 = 저장으로 이해하시면 됩니다.

 

지금까지 printf() 함수는 무조건 출력기능만 있었는데 sprintf() 함수는 독특하게도 문자열에 서식에 맞춰 저장하는 기능이 생겼습니다. 물론 "문자열에 출력" 한다는 관점에서 접근하면 비슷하게 이해할 수 있습니다.

 

 

숫자, 문자, 소수 같은 여러 자료형들이 섞여 있어도 sprintf() 함수를 한번 써버리면 서식문자에 맞춰서 문자열 하나에 통째로 저장이 가능합니다.

이것도 역시 매우 유용하죠.

 

<정리>

printf() 함수 : 서식문자에 맞춰서 모니터(콘솔창)에 출력

sprintf() 함수 : 서식문자에 맞춰서 문자열에 출력(저장)

 

scanf() 함수 : 키보드로부터 서식문자에 맞춰서 입력받고 변수에 저장

sscanf() 함수 : 문자열로부터 서식문자에 맞춰서 입력받고 변수에 저장

 

strtok() 함수

strtok() 함수를 이용하면 구분자(Separator)를 기준으로 문자열 분리가 가능합니다.

이 함수는 특정 구분자를 찾아내서 문자열을 토큰으로 분리할때 사용합니다.

(파이썬 해보신분들은 split() 함수를 생각하시면 됩니다. 모르시면 PASS)

 

예를 들어서 "2011/08/29" 라는 문자열이 있다면 구분자를 "/" 로 선택하면 토큰은 각각 "2011","08","29" 가 됩니다.

 

char * strtok (char * str, const char * sp);

 

strtok() 함수의 원형입니다. str에 저장된 문자열을 sp에 저장된 문자열을 통해 토큰을 출력합니다.

즉, str에서 구분자를 찾아내서 현재 토큰(문자열)의 주소를 반환합니다.

 

strtok() 함수의 두번째 호출부터는 str에 NULL 포인터를 지정하면 이전 호출에서 찾은 구분자 다음위치부터 같은 작업을 반복합니다. 더 이상의 토큰이 없으면 NULL을 반환합니다.

 

#include <stdio.h>
#include <string.h>


int main(){
	char array1[50] = "Good-morning-Good-afternoon-Good-evening";
	char * p = NULL;
	int count = 0;
	
	p = strtok(array1, "-");
	
	for(count = 0; p!= NULL; count++){
		puts(p);
		p=strtok(NULL, "-");
	}
	
	printf("토큰의 개수는 총 %d개입니다. \n", count);
	return 0;
}

Good
morning
Good
afternoon
Good
evening
토큰의 개수는 총 6개입니다.

--------------------------------
Process exited after 0.00438 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

사용법은 위와 같습니다. 개인적인 의견으로는 사용방법이 그다지 직관적이지 못하고 그렇게 마음에 들지도 않아서 저의 경우에는 잘 사용하지 않을거 같습니다.

 

그래도 C언어에 이런게 있다는게 어딥니까.. 만약에 이걸 안쓰면 직접 구현해야 합니다.

 

기타 표준 함수

지금까지 문자열 처리 함수에 관해서 배웠습니다. 다음으로는 데이터를 변환하는 함수에 대해 알아보겠습니다.

데이터 변환 표준 함수

데이터 변환 함수를 사용하려면 헤더 파일 stdlib.h 나 ctype.h에 정의되어 있어 해당 해더 파일을 인클루드 해야 합니다.

 

함수의 원형 설명
double atof(const char * str); 문자열을 double형 데이터로 변환
int atoi(const char * str); 문자열을 int형 데이터로 변환
long atol(const char * str); 문자열을 str형 데이터로 변환

* 사용을 위해서 <stdlib.h> 를 인클루드 합니다.

함수 출력 형태를 보시면 알겠지만 변환된 값을 반환합니다.

 

우선 데이터 변환 함수인 atof(), atoi(), atol()에 대해 알아보겠습니다. stdlib.h 헤더파일을 인클루드 해야 사용 가능합니다. 이름이 조금 생소해서 외우기 힘드실 수 있는데 약자를 알면 좀 편합니다.

 

예를 들어서 atof => Alphabet to Floating Point 입니다.

즉 읽는 방식이 a / to / f 입니다.

atoi 의 경우에도 Alphabet to Integer 

a / to / i 로 읽어주시면 됩니다.

 

atol 도 a / to / l => Alphabet to Long 로 동일합니다.

 

물론 char, ASCII로 설명하는 분들도 있는듯 합니다만

저는 아래 링크를 참고해서 작성했습니다.

 

참고 : https://blog.naver.com/sunjin220/90082127054

 

[C] C언어 약어의 의미

마지막 업데이트 일 : 2015.01.09 키워드 자체에 모든 철자가 나온 경우는 제외함 < Header > conio...

blog.naver.com

 

#include <stdio.h>
#include <stdlib.h>


int main(){
	char * str1 = "3.14";
	char * str2 = "100";
	char * str3 = "10000000";

	double num1 = atof(str1);
	int num2 = atoi(str2);
	long num3 = atol(str3);
	
	printf("%lf, %d, %ld \n", num1, num2, num3);
	
	return 0;
}

3.140000, 100, 10000000

--------------------------------
Process exited after 0.004653 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

원래는 문자열 "100"은 숫자 100이 아니라서 산술연산(사칙연산)이 불가능합니다.

그런데 변환 함수를 활용해 int, double, long 과 같은 타입으로 변환을 해주면 가능해지죠.

 

위 예제를 보시면 원하는대로 변환이 잘 되었습니다.

반환값이 곧 변환된 값이므로 바로 반환값을 변수에 저장하면 됩니다.

 

물론 문자열을 정수, 실수로 변환하는건 아까 배운 만능 함수인 sscanf()를 사용할 수 있습니다만 sscanf()는 기능이 다양한 만큼 무겁고 느린편이라 성능이 그렇게 좋진 못합니다.

 

단순히 한 문자열을 원하는 자료형으로 변환시엔 stdlib.h 를 인클루드하고 위에 ato~ 계열의 함수를 사용하는게 더 효율적입니다.

 

함수의 원형 설명
int toascii(int num); 문자를 ASCII 문자로 변환
int tolower(int num); 문자를 소문자로 변환
int toupper(int num); 문자를 대문자로 변환

* 사용을 위해서 <ctype.h>를 인클루드 합니다.

 

추가로 함수 3개를 더 알아보겠습니다. 다음 함수 toascii(), tolower(), toupper()은 문자를 각각 ASCII / 소문자 / 대문자 로 바꿔주는 함수입니다.

 

#include <stdio.h>
#include <ctype.h>


int main(){
	char a1 = 'A';
	char a2 = 'a';
	
	printf("아스키코드 : %d \n", toascii(a1));
	printf("소문자 : %c \n", tolower(a1));
	printf("대문자 : %c \n", toupper(a2));
	
	return 0;
}

아스키코드 : 65
소문자 : a
대문자 : A

--------------------------------
Process exited after 0.00517 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

사용법은 간단하므로 따로 설명은 하지 않고 넘어가겠습니다.

 

toascii() 함수를 쓰면서 그냥 a1을 %d로 출력만 해주면 되는데 저 함수를 왜 쓰냐고 생각하시는 분들이 있을 수 있습니다. 

 

알고리즘 문제를 푸시다보면 ASCII 코드를 저장하거나 이용해야하는 상황이 나오는데 단순히 printf("%d")로 출력만해서는 저장할 수 없습니다. toascii() 함수를 사용하면 인자값에 제공된 문자에 대한 ASCII 값이 반환되기 때문에 변수에 따로 저장가능합니다.

 

수학 관련 표준 함수

이제부터는 실제로 프로그램을 개발할 때 유용한 수학 관련 시스템 라이브러리 함수를 살펴보겠습니다.

이들을 사용하려면 math.h나 stdlib.h 를 인클루드 해야 합니다.

 

아래 표는 주로 실수를 다루는 함수들이며 math.h를 인클루드하는 수학 관련 함수들의 원형을 정리해본것 입니다.

출처 : https://ko.wikipedia.org/wiki/C_%EC%88%98%EC%8B%9D_%ED%95%A8%EC%88%98

double sin ( double x ); 사인 x를 구한다.
double cos ( double x ); 코사인 x를 구한다.
double tan ( double x ); 탄젠트 x를 구한다.
double asin ( double x ); 아크 사인 x를 구한다.
double acos ( double x ); 아크 코사인 x를 구한다.
double atan ( double x ); 아크 탄젠트 x를 구한다.
double atan2 ( double y, double x ); 아크 탄젠트 y/x를 구한다.
double sinh ( double x ); 하이퍼볼릭 사인 x를 구한다.
double cosh ( double x ); 하이퍼볼릭 코사인 x를 구한다.
double tanh ( double x ); 하이퍼볼릭 탄젠트 x를 구한다.
double exp ( double x ); $e^x$를 구한다.
double frexp ( double x, int * exp ); 지수를 exp가 가리키는 변수에 저장하고 가수를 반환한다.
double ldexp ( double x, int exp ); x * 2exp를 반환한다.
double log ( double x ); loge x를 구한다.
double log10 ( double x ); log10 x를 구한다.
double modf ( double x, double * intpart ); 정수부를 intpart가 가리키는 변수에 저장하고 소수부를 반환한다.
double pow ( double x, double y ); $x^y$를 구한다.
double sqrt ( double x ); $\sqrt{x}$를 구한다.
double ceil ( double x ); x보다 작지 않은 가장 작은 정수를 구한다.
double floor ( double x ); x보다 크지 않은 가장 큰 정수를 구한다.
double fabs (double x); x의 절댓값을 구한다.
double fmod ( double x, double y ); x를 y로 나눈 나머지를 구한다.

* 사용을 위해서 <math.h>를 인클루드 합니다.

 

워낙 양이 방대하므로 예제로 따로 다루진 않겠습니다.

프로그램을 구현하다가 필요할때 위 표나 인터넷 검색을 통해 관련 레퍼런스를 참조해 개발해 주시면 됩니다.

 

또한 fabs()의 경우 double 형태의 값에 대한 절대값을 구하는 함수입니다.

일반적인 정수의 절댓값 함수 abs()의 경우엔 특이하게 fabs()와 같은 헤더파일에 정의되어 있는것이 아닌

stdlib.h에 정의되어 있습니다.

 

pow(), sqrt() 2개 함수의 경우 상당히 유용하게 사용되므로 알아두십시오.

거듭제곱 함수 pow() 경우 While 이나 For문으로 구현할 수 있다지만 루트값을 구하는 sqrt()의 경우 상상만해도 머리가 아파집니다 (...)

 

직접 sqrt() 를 구현하는것에 관심있다면 아래 링크를 참고해주세요.

https://bashsi.tistory.com/entry/CC-%EC%A0%9C%EA%B3%B1%EA%B7%BC-SQRT

 

C/C++ 제곱근 SQRT

제곱근 구하는 함수를 처음 봤는데 구글에 검색하자마자 바로 눈에 보인건 구현 이였다. 그래서 나도 따라해본다(?) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include double SQRT(double input..

bashsi.tistory.com


 

함수 원형 설명
int rand(void); 난수를 생성
int srand(unsinged int seed); seed를 지정하여 난수를 생성

* 사용을 위해서 <stdlib.h>를 인클루드 합니다.

 

rand()와 srand() 함수는 난수를 발생시키는 함수로 난수의 의미는 예측이 불가능하게 생성되는 수를 의미합니다.

즉 random한 숫자들입니다.

 

rand() 함수는 난수의 생성 패턴을 하나로 설정하는것이고, srand() 함수는 나수의 생성패턴을 여러 개로 설정하는 차이가 있습니다.

 

위 2개 함수는 유용하게 사용할 수 있으니 예제를 가지고 다뤄보겠습니다.

 

#include <stdio.h>
#include <stdlib.h>

int main(){
	puts("10개의 난수를 발생시킵니다.");
	
	for(int i = 0; i < 10; i++)
	{
		printf("%d \t", rand());
	}
	return 0;
}

10개의 난수를 발생시킵니다.
41      18467   6334    26500   19169   15724   11478   29358   26962   24464
--------------------------------
Process exited after 0.005619 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

우와! 랜덤한 숫자들이 신기하게 뽑아졌네요

근데 이거 한가지 문제점이 있습니다.

 

프로그램을 껐다가 계속 실행해봐도

41      18467   6334    26500   19169   15724   11478   29358   26962   24464

이러한 값들이 동일하게 계속 나타납니다.

 

마치 이미 정해진 수열을 뽑아내는듯이요.

아마 저위 프로그램을 여러분이 동일하게 작성해도 같은값이 나올 확률이 높습니다.

 

사실 rand() 함수를 호출한다는것은 srand(1); 함수를 호출하고 rand() 함수를 호출한것과 같습니다.

 

위에 표를 보시면 int srand(unsinged int seed); : seed를 지정하여 난수를 생성

라고 srand() 함수의 원형이 설명되어 있습니다.

저 seed가 달라질때마다 뽑히는 숫자가 달라집니다.

 

마인크래프트 같은거 해보신분들은 알겠지만 맵에 시드값을 다르게 주면 그 시드에 맞춰서 맵이 생성되잖아요?? 비슷한 의미 입니다.

 

#include <stdio.h>
#include <stdlib.h>

int main(){
	puts("10개의 난수를 발생시킵니다.");
	srand(2); //시드를 설정 
	
	for(int i = 0; i < 10; i++)
	{
		printf("%d \t", srand(1));
	}
	return 0;
}

10개의 난수를 발생시킵니다.
45      29216   24198   17795   29484   19650   14590   26431   10705   18316
--------------------------------
Process exited after 0.005972 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

srand() 함수를 호출하면 시드에 따라 랜덤한값을 srand()가 직접 반환할거 같지만 그렇지 않습니다.

srand(시드); 로 rand()에 대한 시드값을 설정만하고 rand()를 호출해야 난수가 생성됩니다.

 

#include <stdio.h>
#include <stdlib.h>

int main(){
	puts("10개의 난수를 발생시킵니다.");
	srand(1); //시드를 설정 
	
	for(int i = 0; i < 10; i++)
	{
		printf("%d \t", rand());
	}
	return 0;
}

10개의 난수를 발생시킵니다.
41      18467   6334    26500   19169   15724   11478   29358   26962   24464
--------------------------------
Process exited after 0.005916 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

srand(1); 로 시드를 1로 설정하면 아까 아무것도 안써주고 rand() 함수를 호출했을때와 값이 동일하게 나타납니다. 즉 rand()는 기본적으로 시드가 1인상태로 되어 있다는 것입니다.

 

진정한 난수 생성

물론 srand()에 시드값을 다르게 적어주고 rand() 함수를 호출하면 값이 달라지긴 합니다만

시드값 역시 매번다른값이 들어가거나 난수값이 들어가야 합니다.

 

난수를 만들기 위해 난수를 만든다? 너무 모순적이죠?

이것에 대한 한가지 해결법으로 많이 나와있는 방법인 시간을 사용하는 방법이 있습니다.

 

시간이 매초 바뀌는것을 이용해 현재 시간을 가져오고 그것을 srand()의 seed 값으로 제공하여 난수값을 생성하는 방법입니다

 

관련 내용을 구현한 블로거분 한분 링크를 올려드리니 궁금하신분들은 한번 참고해보시길 바랍니다.

 

https://reakwon.tistory.com/63

 

[C언어] 난수생성 rand, srand, time 함수 설명과 사용방법 예제

우리는 난수를 사용하고 싶을 때가 있습니다. 하지만 이런 기능을 만드는 것은 좀처럼 쉬운 일은 아니지요. 귀찮기도 하구요. C언어에서는 그러한 프로그래머를 위해서 난수생성함수를 제공하

reakwon.tistory.com

 

rand()함수의 난수 발생 범위

rand() 함수의 난수 발생 범위는 일반적으로 0~32767까지 설정되어 있습니다.

 

범위를 직접 지정하고 싶으시면

 

1~a의 범위를 뽑아야할때

randNum = rand() % a + 1; 과 같은식으로 나머지 연산(mod)을 이용하시면 됩니다.

 

수학적인 내용이지만 0~49까지 뽑고싶을때 50으로 나머지 연산을 하면

0~49까지는 50으로 나눴을때 나머지가 자기자신이라 그대로 나오게 되고 50은 0, 더큰수들은 어떻게 하던 50보다 작은수가 나오는것을 알 수 있습니다.

 

마지막에 1 더해주면 범위가 1~50이 되겠죠?

 

원리가 잘 이해 안되면 그냥 외워서 쓰셔도 무방합니다.

컴퓨터에선 나머지 연산을 쓰는걸 좋아하니 알아두시면 좋습니다

 


문자열 표준 함수와 기타 표준함수는 여기까지입니다!

다음편에서는 이제 콘솔 입출력과 파일 입출력을 다뤄보도록 하겠습니다.

 

감사합니다 ㅎㅎ

COMMENT WRITE