Programming/C

C언어 - 문자와 문자열 함수

JunsuKim 2021. 10. 9.
728x90

getchar, putchar

getchar와 putchar는 문자 입출력 함수이다.

getchar는 문자 입력 함수이며, stdin으로 표현되는 표준 입력 스트림으로부터 하나의 문자를 입력받아 반환한다.

함수 호출을 성공할 시 쓰인 문자정보가 반환되며, 실패 시 EOF가 반환된다.

putchar는 문자 출력 함수이며, 인자로 전달된 문자 정보를 stdout으로 표현되는 표준 출력 스트림으로 전송하는 함수이다. 파일의 끝에 도달하거나 함수 호출 실패 시 EOF를 반환한다.

다음의 예제를 보자.

#include<stdio.h>

int main() {
	while (1) {
		int ch = getchar();
		if (ch == EOF) break;
		putchar(ch);
	}
	return 0;
}

코드를 보면 getchar는 int형 변수에 문자를 저장하고 있다.

문자를 저장하기 위해서는 char를 써야 하는데 왜 int형 변수에 저장하는 것일까?

이는 getchar 함수의 반환형이 int이기 때문이다.

또한 getchar 함수가 호출된다 해서 하나의 문자만 입력받지도 않았다.

이는 문장이 입력되면 문장을 구성하는 문자 수만큼 getchar 함수가 호출되면서 모든 문자를 읽기 때문이다. 

fgetc, fputc

fgetc 함수와 fputc 함수 또한 문자 입출력 함수이다. 하지만 getchar, putchar와는 차이점이 있다.

바로 문자를 입력받거나, 전송할 스트림을 지정할 수 있는 것이다. 즉 stdin, stdout뿐만이 아니라 파일을 대상으로 데이터를 전송, 반환받을 수 있는 것이다. 이 두 함수는 파일 입출력을 공부할 때 깊숙이 알아보도록 하겠다.

EOF

EOF는 End Of FIle의 약자이다. 파일의 끝을 표현하기 의해 정의된 상수이므로 파일을 대상으로 fgetc 함수가 호출됐을 때 그 결과가 EOF라면 파일의 끝에 도달해 더 읽을 내용이 없다는 뜻이 된다.

이 뿐만 아니라 EOF는 getchar, putchar 함수를 설명할 때 봤던 코드에서도 사용됐듯이, 키보드를 대상으로 하는 fgetc, getchar 함수에서도 반환이 된다.

  • 함수 호출이 실패했을 경우
  • Windows에서 Ctrl+z키, Linux에서 Ctrl+d키가 입력됐을 경우

키보드의 입력에서는 파일의 끝이 존재할 수 없으므로 반환시기를 약속해놓은 것이다.

 

또한 EOF는 -1로 정의되어 있다. 따라서 getchar, putchar가 int형을 반환하는 이유는 EOF를 반환할 때 -1을 인식할 수 있도록 int형으로 반환형을 정의해 놓은 것이다.

 

여기까지는 문자 단위의 입출력 함수를 알아보았다. 이제부터는 문자열 단위의 입출력 함수를 알아볼 것이다.

gets, puts

printf와 scanf 함수를 이용해도 문자열의 입출력은 가능하다. 하지만 gets와 puts 함수는 앞의 두 함수랑 성격이 다르다.

scanf 함수는 공백이 포함된 형태의 문자열을 입력하는데 제한이 있지만 gets 함수에서는 공백을 포함하는 문자열도 입력받을 수 있다.

gets 함수는 파일의 끝에 도달하거나 함수 호출 실패 시 NULL 포인터를 반환한다.

puts 함수는 성공 시 음수가 아닌 값을, 실패 시 EOF를 반환한다.

사용법은 다음과 같다.

#include<stdio.h>

int main() {
	char str[50];
	gets(str);
	puts(str);
	return 0;
}

코드를 보면 문장 구성이 확실히 간단하다. 하지만 gets 함수는 미리 선언해 놓은 배열의 길이를 넘어서면 안 된다. 

이를 넘어서게 된다면 할당받지 않은 메모리 공간을 침범하여 실행 중 오류가 발생하게 된다.

puts 함수는 호출이 되면 문자열 출력 후 자동으로 개행이 이뤄진다.

fgets, fputs

위의 fgetc, fputc와 같이 이 두 함수도 파일 입출력을 할 때 주로 사용되는 함수들이다.

그러므로 간단히만 알아보고 파일 입출력을 공부할 때 더 자세히 다뤄보도록 하겠다.

fgets 함수는 "stdin으로부터 문자열을 입력받아서 배열에 저장하되, sizeof(배열이름)의 길이만큼만 저장해라"를 의미한다.

예를 들어보자.

#include<stdio.h>

int main() {
    char str[7];
    fgets(str, sizeof(str), stdin);
    puts(str);
    return 0;
}

str의 길이가 7이므로 그 이상부터는 출력이 되지 않는다. 하지만 결과에서는 7까지 출력을 하지 않고 6까지만 출력하였다. 이는 문자열에서 마지막엔 자동으로 널 문자가 추가되기 때문이다.

 

fputs 함수는 fputc함수처럼 두 번째 인자를 통해 출력의 대상을 결정할 수 있다. 

puts함수에서는 호출 후 자동으로 개행이 이뤄지나 fputs 함수는 자동적으로 개행이 이뤄지지 않아 직접 개행을 해줘야 한다.

입출력 이외의 문자열 관련 함수

이번에는 헤더파일 string.h에 선언된 문자열 관련 함수들 중 사용 빈도수가 높은 함수들을 예제를 보며 알아보자.

1. strlen 함수

strlen함수는 인자로 전달된 문자열의 길이를 반환하는 함수이다.

예제를 보자.

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

int main() {
    char perID[7];
    gets(perID);
    int len = strlen(perID);
    printf("%d", len);
    return 0;
}

2. strcpy, strncpy

이 두 함수는 문자열의 복사에 사용되는 함수들이다.

strcpy를 사용한 예제를 보자.

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

int main() {
    char str1[30] = "Simple String";
    char str2[30];
    strcpy(str2, str1);
    printf("str1: %s\n", str1);
    printf("str2: %s\n", str2);
    return 0;
}

복사할 문자열의 길이가 복사될 배열의 길이보다 길지 않도록 주의를 해야 한다.

이제 strncpy 함수를 보자.

이 함수가 의미하는 바는 "str1에 저장된 문자열을 str2에 복사하되, str1의 길이가 매우 길다면, sizeof(str2)가 반환한 값에 해당하는 문자의 수만큼만 복사해라."이다.

하지만 주의 할 점이 있는데, 예제를 봐보자.

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

int main() {
    char str1[30] = "Simple String";
    char str2[7];
    printf("\n");
    strncpy(str2, str1, sizeof(str2));
    printf(" str1: %s\n", str1);
    printf(" str2: %s\n", str2);
    return 0;
}

strncpy(str2, str1, sizeof(str2));

이 문장을 보면 "복사하는 최대 문자 수가 sizeof(str2)의 반환 값이 전달되었으니, 할당된 배열을 넘어서서 복사가 이뤄지지 않겠구나"라고 생각할 수 있다. 하지만 결과를 보면 출력이 이상하게 된 것을 볼 수 있다.

실제로 배열을 넘어서서 복사가 이뤄지지는 않았지만, 배열의 크기 안에 널 문자가 포함되지 않는다는 문제가 있다.

널 문자가 존재해야 널 문자 이전까지 출력을 하는데, 존재하지 않으므로 엉뚱한 영역까지 출력되는 것이다.

이 문제를 해결하기 위해서는 다음과 같이 해야 한다.

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

int main() {
    char str1[30] = "Simple String";
    char str2[7];
    printf("\n");
    strncpy(str2, str1, sizeof(str2) - 1);
    str2[sizeof(str2) - 1] = 0;
    printf(" %s\n", str2);
    return 0;
}

이와 같이 배열의 실제 길이보다 1만큼 작은 값을 전달해서 널문자가 삽입될 공간을 남겨두고 복사를 진행한 후,

배열의 끝에 널문자를 삽입하는 것이다.

3. strcat, strncat

이 함수들은 문자열의 뒤에 다른 문자열을 붙여주는(복사하는) 기능을 제공한다.

쉽게 "str1에 저장된 문자열 뒤에 str2에 저장된 문자열을 붙여라."인 것이다.

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

int main() {
    char str1[30] = "Simple ";
    char str2[7] = "String";
    printf("\n");
    strcat(str1, str2);
    printf("%s", str1);
    return 0;
}

str1에 저장된 문자열 뒤에 str2에 저장된 문자열을 붙이는 위치는 str1의 널 문자가 있는 위치부터이다.

그래야만 붙였을 때 문자열의 끝에만 널 문자가 존재하기 때문이다.

이어서 strncat 함수를 보자.

이 함수는 뒤에 붙일 문자열의 길이를 정할 수 있다.

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

int main() {
    char str1[30] = "Simple num: ";
    char str2[30] = "123456789";
    strncat(str1, str2, 7);
    printf(" %s", str1);
    return 0;
}

strncpy 함수는 문자열의 끝에 널 문자를 자동으로 삽입해준다.

4. strcmp, strncmp

이들은 문자열을 비교하는 함수이다.

우선 다음의 코드를 보자.

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

int main() {
    char str1[] = "My String";
    char str2[] = "My String";

    if (str1 == str2) puts("eqaul");
    else puts("not equal");
    return 0;
}

이 코드의 결과는 "not equal"이다.

배열의 이름은 배열의 주소 값을 의미하므로 문자열의 내용이 같아도 not equal이 뜨게 된다.

문자열의 내용을 비교하고자 한다면 strcmp 또는 strncmp 함수를 써야 한다.

  • str1이 더 크면 0보다 큰 값 반환
  • str2가 더 크면 0보다 작은 값 반환
  • 내용이 모두 같으면 0 반환

여기서 문자열의 크고 작음은 아스키코드 값을 기준으로 결정하며, 반환 값이 0이 아니라면 다른 값인 것이다.

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

int main() {
    char str1[20];
    char str2[20];
    printf("문자열 입력: ");
    scanf("%s", str1);
    printf("문자열 입력: ");
    scanf("%s", str2);

    if (!strcmp(str1, str2)) puts("완전 동일");
    else {
        if (!strncmp(str1, str2, 3)) puts("앞의 3글자만 같음");
        else puts("동일하지 않음");
    }

    return 0;
}

atoi, atol, atof

이 함수들은 헤더파일 stdlib.h에 선언된 함수들이다.

문자열 "123"을  정수형 123으로 변환하거나, "7.15"를 실수형 7.15로 변환할 때 사용된다.

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

int main() {
    char str[20];
    printf("정수 입력: ");
    scanf("%s", str);
    printf("%d\n", atoi(str));

    printf("실수 입력: ");
    scanf("%s", str);
    printf("%g\n", atof(str));
    return 0;
}

728x90

댓글