본문 바로가기
Language&Framework&Etc/C

문자와 문자열 관련 함수(21-5) 입출력 이외의 문자열 관련 함수

by 머리올리자 2020. 12. 11.

문자열의 길이를 반환하는 함수 : strlen

#include <string.h>
size_t strlen(const char* s)

- 전달된 문자열의 길이를 반환하되, 널 문자는 길이에 포함하지 않는다.

 

위 함수의 반환형 size_t는 일반적으로 다음과 같이 선언

typedef unsigned int size_t

typedef는 나중에 공부함

당분간은 "unsigned int의 선언을 size_t로 대신할 수 있다" 라는 것만 기억

 

typedef 선언으로 인해서 size_t가 unsigned int를 대신할 수 있게 된 것이다.

 

즉, 두 선언은 완전히 동일하다

size_t len;
unsigned int len;

호출 방법

int main(void)
{
	char str[] = "1234567";
	printf("u \n", strlen(str));
}

strlen 함수의 반환형은 size_t이니, 이 함수의 반환 값을 unsigned int형 변수에 저장하고 서식문자 %u로 출력하는 것이 정확하다.

 

하지만 문자열 길이정보 또한 int형으로 저장 가능하기 때문에 %d로 출력 하는 것도 가능하며 더 흔한 일이다.

 

만약 "fgets 함수호출을 통해서 문자열을 입력 받고 싶은데, 같이 딸려서 들어오는 \n 문자는 문자열에서 제외시키고 싶을 때"

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

void removebsn(char str[]);

int main(void)
{
	char str[100];
	printf("문자열 입력 : ");
	fgets(str, sizeof(str), stdin); // fgets 함수호출을 통해서 문자열을 입력 받고 있다. 따라서 \n 문자가 문자열의 일부로 포함된다.
	printf("길이: %d, 내용: %s \n", strlen(str), str);

	removebsn(str);
	printf("길이: %d, 내용: %s \n", strlen(str), str);

	return 0;
}

void removebsn(char str[])
{
	int len = strlen(str);
	str[len - 1] = 0;
}

처음 출력할 때에 개행이 두 번 이뤄졌다. 

 

이는 removebsn 함수를 통해서 \n 문자가 소멸되었기 때문이다.

 

문자열을 복사하는 함수들: strcpy, strncpy

#include <string.h>
char * strcpy(char * dest, const char * src);
char * strncpy(char * dest, const char * src, size_t n);

- 복사된 문자열 주소 값 반환

 

strcpy의 형태

int main(void)
{
	char str1[30] = "simple string";
	char str2[30];
	strcpy(str2, str1);
}

str2에는 str1이 저장하고 있는 문자열이 복사된다.

물론 문자열이 복사될 배열의 길이가 문자열의 길이보다 작지 않도록 주의

 

strncpy의 형태

int main(void)
{
	char str1[30] = "simple string";
	char str2[30];
	strncpy(str2, str1, sizeof(str2));
}

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

 

strncpy 함수는 복사될 배열의 길이를 넘어서지 않는 범위 내에서 복사를 진행하고자 하는 경우에 유용하다.

 

이렇듯 strncpy 함수는 복사될 배열의 길이를 넘어서지 않는 범위 내에서 복사를 진행하고자 하는 경우에 유용하다.

 

주의해야 할 점 예제.

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

int main(void)
{
	char str1[20] = "1234567890";
	char str2[20];
	char str3[5];

	/*** case 1 ***/
	strcpy(str2, str1);
	puts(str2);

	/*** case 2 ***/
	strncpy(str3, str1, sizeof(str3));
	puts(str3);

	/*** case 3 ***/
	strncpy(str3, str1, sizeof(str3) - 1);
	str3[sizeof(str3) - 1] = 0;
	puts(str3);

	return 0;
}

strncpy(str3, str1, sizeof(str3));

"복사하는 최대 문자의 수로 sizeof(str3)의 반환 값이 전달되었으니, 할당된 배열을 넘어서서 복사가 이뤄지지 않겠구나"

 

실제로 배열을 넘어서서 복사가 이뤄지지는 않으니 이것이 잘못된 판단은 아니다.

 

즉, 배열 str3의 길이가 총 5이니 총 5개의 문자가 복사된다.

 

단! 이 5개의 문자 안에 NULL 문자가 포함되지 않는다는 문제가 있다.

 

strncpy 함수는 문자열을 단순하게 복사한다.

 

5개의 문자를 복사하라고 하면 앞에서부터 딱 5개의 문자만 복사한다.

 

마지막 문자가 NULL 문자인지 아닌지는 상관하지 않는다.

 

따라서 위의 문장 실행 후 str3에 저장되는 다섯 개의 문자는 1, 2, 3, 4, 5와 같다

 

그래서 출력결과가 이상한 것이다. NULL 문자가 존재해야 NULL 문자 이전까지 출력을 할 텐데, 널 문자가 존재하지 않으니 엉뚱한 영역까지 출력을 하는 것이다.

 

그럼 strncpy 함수는 어떻게 호출해야 할까?

strncpy(str3, str1, sizeof(str3)-1);
str3[sizeof(str3)-1]=0;

위와 같이 호출을 해야 한다.

 

strncpy 함수의 세 번째 인자로 배열의 실제길이보다 하나 작은 값을 전달

NULL 문자가 삽입될 공간을 남겨둠

그리고 배열의 끝에 NULL 문자를 삽입

 

문자열을 덧붙이는 함수들: strcat, strncat

문자열의 뒤에 다른 문자열을 복사하는 기능을 제공

 

"str1에 저장된 문자열 뒤에 str2에 저장된 문자열을 복사좀 해주라"

#include <string.h>
char * strcat(char * dest, const char * src);
char * strncat(char * dest, const char * src, size_t n);

strcat 호출 형태

int main(void)
{
	char str1[30] = "First~";
	char str2[30] = "Second";
	strcat(str1, str2) // str1의 문자열 뒤에 str2를 복사
}

위의 형태로 strcat 함수가 호출되면 str2의 문자열이 str1의 문자열 뒤에 덧붙여지는데, 덧붙여지는 형태는 다음과 같다.

(NULL 문자가 어떻게 처리되는지 주의)

 

 

덧붙임이 시작되는 위치는 NULL 문자 다음이 아닌, NULL 문자가 저장된 위치에서부터이다.

 

이렇듯 NULL 문자가 저장된 위치에서부터 복사가 진행되어야 덧붙임 이후에도 문자열의 끝에 하나의 NULL문자만 존재하는 정상적인 문자열

 

strncat 호출 형태

strncat(str1, str2, 8);

"str2의 문자열 중 최대 8개를 str1의 뒤에 덧붙여라!"

 

즉, str2의 길이가 8을 넘어선다면 8개의 문자까지만 str1에 덧붙이라는 의미

 

그러나 8개의 문자에는 NULL 문자가 포함되지 않는다.

 

따라서 NULL 문자를 포함하여 실제로는 총 9개의 문자가 str1에 덧붙여진다.

 

이렇듯 strncpy 함수와 달리 strncat 함수는 문자열의 끝에 NULL 문자를 자동으로 삽입해준다.

 

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

int main(void)
{
	char str1[20] = "First~";
	char str2[20] = "Second";

	char str3[20] = "Simple num: ";
	char str4[20] = "1234567890";


	/*** case 1 ***/
	strcat(str1, str2);
	puts(str1);

	/*** case 2 ***/
	strncat(str3, str4, 7);
	puts(str3);

	return 0;
}

문자열을 비교하는 함수들: strcmp, strncmp

#include <stdio.h>

int main(void)
{
	char str1[] = "My string";
	char str2[] = "My string";
	if (str1 == str2)
		puts("equal");
	else
		puts("not equal");

	return 0;
}

위 코드를 보자

이는 str1과 str2의 문자열의 내용을 비교하는 것이 아니라

배열 str1과 배열 str2의 주소 값을 비교하는 것이다.

 

문자열을 비교하자면 별도의 함수를 호출해야 한다.

#include <string.h>
int * strcmp(const char * s1, const char * s2);
int * strncmp(const char * s1, const char * s2, size_t n);

- 두 문자열의 내용이 같으면 O, 같지 않으면 0이 아닌 값 반환

 

위 두 함수 모두 인자로 전달된 두 문자열의 내용을 비교하여 다음의 결과를 반환.

 

단, strncmp 함수는 세 번째 인자로 전달된 수의 크기만큼만 문자를 비교.

즉, strncmp 함수를 호출하면 앞에서부터 시작해서 중간부분까지 부분적으로만 문자열을 비교할 수 있음

 

  • s1이 더 크면 0보다 큰 값 반환
  • s2가 더 크면 0보다 작은 값 반환
  • s1과 s2의 내용이 모두 같으면 0 반환

문자열의 크고 작음은 아스키 코드 값을 기준으로 결정된다.

 

"ABCD"

"ABCC"

 

세 번째 문자까지 동일하다

네 번째 문자를 비교하면 D의 아스키 코드 값이 C의 아스키 코드 값보다 크다.

 

따라서 "ABCD"가 더 큰 문자열이 되어 다음의 실행결과로 양수가 출력된다.

printf("%d", strcmp("ABCD", "ABCC"));

참고로 양수가 반환되어야 한다는 사실만 표준으로 정의되어 있을 뿐, 그 값이 구체적으로 얼마가 되어야 한다는 사실까지는 정의 되어 있지 않다.

 

따라서 양수가 반환된다는 사실만을 근거로 코드를 작성

 

다음 두 문자열 비교

 

"ABCD"

"ABCDE"

 

네 번째 문자까지 동일

 

마지막 다섯 번째 문자

 

ABCD : NULL 문자

ABCDE : E

 

E의 아스키 코드 값은 NULL 아스키 코드 값인 0보다 크다.

 

예시.

#define _CRT_SECURE_NO_WARNINGS

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

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

	if (!strcmp(str1, str2)) // strcmp 동일하면 0 반환 -> ! -> 동일하면 1반환
	{
		puts("두 문자열은 완벽히 동일");
	}
	else
	{
		puts("두 문자열은 동일하지 않는다.");

		if (!strncmp(str1, str2, 3))
			puts("그러나 앞에 세 글자는 동일합니다.");
	}
	return 0;
}

그 이외의 변환 함수들

헤더파일 <stdlib.h>에 선언된 함수들

int atoi(const char * str); 문자열의 내용을 int형으로 변환
long atol(const char * str); 문자열의 내용을 long형으로 변환
double atof(const char * str); 문자열의 내용을 double형으로 변환

문자열로 표현된 정수나 실수의 값을 해당 정수나 실수의 데이터로 변환해야 하는 경우가 간혹 있음.

예를 들어 문자열 "123"을 정수 123으로 변환하거나 문자열 "7.15"를 실수 7.15로 변환해야 하는 경우 등...

 

아래 예제를 이용하면 쉽게 해결 가능

#define _CRT_SECURE_NO_WARNINGS

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

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

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

	return 0;
}

 

 

참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.