2-3 문자열

do·2022년 3월 13일
0

API

목록 보기
8/42

과제 - 1

my_strcpy 함수 구현

int my_strcpy(char* dest, size_t dest_buf_size, const char* src, size_t src_buf_size)
{
	size_t temp = 0;
	strlen(src) < src_buf_size
		? (strlen(src) > dest_buf_size ? (temp = dest_buf_size-1) : (temp = strlen(src)))
		: (src_buf_size > dest_buf_size ? (temp = dest_buf_size-1) : (temp = src_buf_size));
	for (int i=0; i<temp; i++)
		dest[i] = src[i];
	dest[temp] = '\0'; //널문자까지 복사됨
	return 0;
}

과제 - 2

my_strcat 함수 구현

char* my_strcat(char* str, const char* add)
{
	char* c;
    c = str; //==&str[0]
	while (*str!='\0'){
		*c = *str;
        c++;
        str++;
	}
	while (*add)
		*(c++) = *(add++);
	*c = '\0';
	return c;
}

my_strstr 함수 구현

char* my_strstr(const char* str, const char* find)
{
	char* c = NULL;
	if (*find=='\0')      //find가 길이 0이면 str을 리턴함
		return (char*)str;
	while (*str){
		if (c!=NULL && *find=='\0')
			break;
		if (c!=NULL && *str!=*find)
			c=NULL;
		if (c==NULL && *str==*find)
			c=(char*)str;  //==&str[0]; find의 첫번째 표시 시작 위치에 대한 포인터를 리턴함
		if (c!=NULL && *str==*find)
			find++;
		str++;
	}
	return (c!=NULL) ? c : NULL;    //find가 str에 없으면 NULL을 리턴함
}

my_strtok 함수 구현

char* my_strtok(char* str, const char* delim)
{
	static char* str_copy = NULL;
	char* delim_copy = NULL;
    
	(str!=NULL) ? (str_copy=str) : (str=str_copy);
    //str에 NULL이 들어올 경우 이전에 찾은 구분자 뒤에서부터 다시 시작함
    
	if (*str_copy=='\0')
		return NULL;
	while (*str_copy){
		delim_copy = (char*)delim;
		while (*delim_copy){
			if (*str_copy==*delim_copy){
				*str_copy='\0'; //알맞은 구분자를 찾으면 NULL로 바꿈
				str_copy++;
				return str; //자른 문자열 리턴함
			}
			delim_copy++;
			}
		str_copy++;
	}
	return str;
}

main 함수에 테스트

int main()
{
//test for my_strcpy()
	char dest[] = "0123456789";
	char src[] = "hello";
	my_strcpy(dest, 4, src, 8);
	puts(dest); //출력 - hel

	char dest2[] = "0123456789";
	char src2[] = "hello";
	my_strcpy(dest2, 5, src2, 4);
	puts(dest2); //출력 - hell

	char dest3[] = "0123456789";
	char src3[] = "hello";
	my_strcpy(dest3, 10, src3, 8);
	puts(dest3); //출력 - hello

	char dest4[] = "0123456789";
	char src4[] = "hello";
	my_strcpy(dest4, 8, src4, 10);
	puts(dest4); //출력 - hello

//test for my_strcat()
	char str1[] = "String";
	char str2[] = "Concatenate";
	my_strcat(str1, str2);
	puts(str1); //출력 - StringConcatenate

//test for my_strstr()
	char* str3 = "This is my number haha";
	char* str4 = "number";
	printf("%s\n", my_strstr(str3, str4)); //출력 - number haha

//test for my_strtok()
	char str5[] = "My, Name, is, Do";
	char* temp = my_strtok(str5, ", ");
	while (temp != NULL){
		printf("%s", temp); //출력 - MyNameisDo
		temp = my_strtok(NULL, ", ");
	}
	puts("");
	return 0;
}         

과제 - 3

수강 신청 입력 양식 변경

  1. 과목명 입력받아 공백 제거하기
  2. 콤마로 분할하기
  3. 존재하는 과목명인지 체크하기
  4. 분할된거 양식 맞춰 이어붙이기
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { MIN_ID_NUM=1, MAX_ID_NUM=100, MAX_INFO_NUM=5 };

typedef struct Info {
	int id;
	char subjects[116];
} Info;

const char* Name_Subjects =
"컴퓨터개론, 이산수학, C언어, JAVA초급,
리눅스구조, 자료구조, 컴파일러, 네트워크개론"; //115

int IDRangeCheck(Info* info)
{
	if (info->id < MIN_ID_NUM || info->id > MAX_ID_NUM){
		puts("ID의 범위는 1부터 100입니다.");
		return -1;
	}
	return 0;
}

int CountCheck(int* num)
{
	if (*num==MAX_INFO_NUM){
		puts("수강 신청 정보가 5개를 초과하였습니다.");
		return -1;
	}
	return 0;
}

int NameCheck(char* str)
{
	if (strstr(Name_Subjects, str)==NULL){
		puts("존재하지 않은 과목명을 입력하였습니다.");
		return -1;
	}
	return 0;
}

int Menu()
{
	int select;
	do{
		puts("======================");
		puts("1) 수강 신청 정보 입력");
		puts("2) 수강 신청 정보 출력");
		puts("3) 종료");
		printf("Select ==> ");
		scanf("%d", &select);
	} while (select!=1 && select!=2 && select!=3);
	return select;
}

int SetInfo(Info* info)
{
	char copystr[116];
	int j=0;
	copystr[0] = '\0';
	info->subjects[0] = '\0';

	printf("ID: ");
	scanf("%d", &info->id);
	if (IDRangeCheck(info)) //ID범위 체크
		return -1;

	printf("수강 신청 과목: ");
	getchar();
	fflush(stdin);
	gets(copystr);

	for (int i=0; i<sizeof(copystr); i++) //공백 제거
		if (copystr[i]!=' ')
			copystr[j++]=copystr[i];

	char* temp = strtok(copystr, ","); //콤마 분할
    
	while (temp!=NULL){
		if (NameCheck(temp)) //과목명 체크
			return -1;
		strcat(info->subjects, temp); //과목명 이어붙이기
		temp = strtok(NULL, ",");
		if (temp!=NULL)
			strcat(info->subjects, ", "); //마지막 과목 빼고 ", " 덧붙이기
	}
	return 0;
}

void GetInfo(Info* info)
{
	printf("ID: %d, 수강신청 과목: ", info->id);
	puts(info->subjects);
}

int main()
{
	Info data[MAX_INFO_NUM];
	int count = 0;

	while(1)
	{
		switch(Menu())
		{
			case 1:
				{
					if (CountCheck(&count))
						break;
					if (!SetInfo(&data[count]))
						count++;
				}
				break;
			case 2:
				{
					if (!count)
						puts("등록된 정보가 없습니다.");
					for (int i=0; i<count; i++)
						GetInfo(&data[i]);
				}
				break;
			case 3:
				{
					puts("프로그램을 종료합니다.");
					return 0;
				}
		}
	}
	return 0;
}

결과

학습 내용

1-1. strcpy()

기능.
src에 있는 문자열을 dest로 복사합니다.
strcpy()는 문자열부터 널문자('\0')까지 복사합니다. dest는 src를 모두 복사 받을 수 있을 정도로 커야하기 때문에, 반드시 복사할 문자열의 크기를 검사해야 합니다.
만약 src 문자열의 길이가 dest 버퍼의 크기-1 보다 크면 버퍼 오버플로우가 발생합니다.
헤더. string.h
원형. char* strcpy(char* dest, const char* src)
매개변수1. char* dest 문자열을 복사할 버퍼
매개변수2. const char* src 원본 문자열
리턴값. char* 목적지 문자열 dest에 대한 포인터를 반환합니다.
예제.

char origin[] = "DoheeKim";
char dest[] = "dest_example";
strcpy(dest, origin);
printf("strcpy before: %s\n", dest);
dest[8] = 'X';
printf("strcpy after: %s\n", dest);
/*출력결과:
DoheeKim
DoheeKimXple*/

1-2. strncpy()

기능. 복사할 문자열의 크기를 정할 수 있으므로 버퍼 오버플로우에 더 안전합니다.
1. n <= sizeof(src) 널문자('\0')는 복사된 스트링에 추가되지 않습니다.
2. n > sizeof(src) dest의 남는 공간은 null로 채워집니다.
3. n <= sizeof(dest) 조건을 벗어날 시 런타임 에러
원형. char* strncpy(char* dest, const char* src, size_t n)
매개변수1. char* dest 문자열을 복사할 버퍼
매개변수2. const char* src 원본 문자열
매개변수3. size_t n 문자열의 크기
리턴값. char* 목적지 문자열 dest에 대한 포인터를 반환합니다.
예제.

char origin[] = "Hello";
char dest[100];
strncpy(dest, origin, sizeof(origin));

1-3. strlen()

기능. 종료 널문자(\0)를 제외하고 string 길이를 판별합니다.
원형. size_t strlen(const char* string)
매개변수. const char* string
리턴값. size_t 문자열 길이

2-1. strcmp()

기능. 두 문자열을 비교합니다.
원형. int strcmp(const char* str1, const char* str2)
매개변수1. const char* str1 비교할 문자열1
매개변수2. const char* str2 비교할 문자열2
리턴값. str1과 str2가 일치하면 0을 리턴합니다.
str1이 str2보다 크다면 0보다 큰 값을 리턴합니다.
str1이 str2보다 작다면 0보다 작은 값을 리턴합니다.

2-2. strncmp()

기능. 길이를 지정해서 두 문자열을 비교합니다.
원형. int strcmp(const char* str1, const char* str2, size_t n)
매개변수1. const char* str1 비교할 문자열1
매개변수2. const char* str2 비교할 문자열2
매개변수3. size_t n 비교할 문자열 길이
(str1, str2보다 큰 값을 n에 넣게 되면, 알아서 문자열 전체를 비교합니다.)
리턴값. str1과 str2가 일치하면 0을 리턴합니다.
str1이 str2보다 크다면 0보다 큰 값을 리턴합니다.
str1이 str2보다 작다면 0보다 작은 값을 리턴합니다.
예제.

const char* str1 = "DoheeHi";
const char* str1 = "DoheeBye";
strncmp(str1, str2, 5); //"Dohee"까지만 검사하므로 0 반환
strncmp(str1, str2, 6); //H > B 이므로 양수 반환

2-3. strcasecmp()

기능. 대소문자를 구분하지 않고 두 문자열을 비교합니다.
원형. int strcasecmp(const char* str1, const char* str2)
리턴값. str1과 str2가 일치하면 0을 리턴합니다.
str1이 str2보다 크다면 0보다 큰 값을 리턴합니다.
str1이 str2보다 작다면 0보다 작은 값을 리턴합니다.
예제.

char *str1 = "STRING";
char *str2 = "string";
strcasecmp(str1, str2); //return 0;

2-4. strncasecmp()

기능. 대소문자를 구분하지 않고 길이를 지정해서 두 문자열을 비교합니다.
원형. int strncasecmp(const char* str1, const char* str2, size_t n)
리턴값. str1과 str2가 일치하면 0을 리턴합니다.
str1이 str2보다 크다면 0보다 큰 값을 리턴합니다.
str1이 str2보다 작다면 0보다 작은 값을 리턴합니다.

3-1. strcat()

기능. 문자열을 연결합니다. add를 str에 뒤쪽에 연결하고, str 끝에 있는 널문자('\0')는 사라지고 바로 add가 붙습니다.
strcat() 함수는 널로 끝나는 스트링에서만 작동합니다. 길이 검사는 수행하지 않습니다. add가 리터럴 스트링일 수 있지만, str 값에 대한 리터럴 스트링을 사용해서는 안 됩니다.
str의 기억장치가 add의 기억장치와 겹치면 작동은 정의되지 않습니다.
원형. char* strcat(char* str, const char* add)
리턴값. char* 연결된 스트링에 대한 포인터를 리턴합니다. (str)
예제.

char str[20] = "Kim";
char add[] = "Dohee";
strcat(str, add); //"KimDohee"

3-2. strncat()

기능. 길이를 지정해서 문자열을 연결합니다. add에 n만큼만 잘라서 넣어도, 문자열 끝에는 항상 '\0'가 붙습니다.
원형. char* strncat(char *str, const char *add, size_t n)
매개변수3. size_t n add의 문자 n번째까지 붙입니다.
리턴값. char* 연결된 스트링에 대한 포인터를 리턴합니다. (str)
예제.

char str[20] = "Kim";
char add[] = "Dohee";
strncat(str, add, 2); //"KimDo"

4-1. strchr()

기능. 문자열에서 특정 문자를 찾습니다.
원형. char *strchr(const char *string, int c)
매개변수1. const char *string 검색할 문자열
매개변수2. int c 존재하는지 확인할 문자 (아스키값으로 들어감)
리턴값. char* 문자열에서 첫번째로 찾은 문자 c의 포인터를 리턴합니다.
찾지 못했다면 NULL을 리턴합니다.
예제.

char str[] = "Dobee";
char* ptr = strchr(str, 'b'); //알파벳 b를 찾음
if (ptr != NULL)
	printf("%c, %d", *ptr, ptr); //b bee

4-2. strrchr()

기능. 문자열에서 특정 문자를 (reverse)찾습니다.
원형. char *strrchr(const char *string, int c)
리턴값. char* 문자열에서 마지막으로 있는 문자 c의 포인터를 리턴합니다.
찾지 못했다면 NULL을 리턴합니다.
예제.

#define SIZE 40
char buffer1[SIZE] = "computer program";
char* ptr;
int ch = 'p';
ptr = strchr(buffer1, ch);
printf("The first occurrence of %c in '%s' is '%s'\n", ch, buffer1, ptr);
//The first occurrence of p in 'computer program' is 'puter program'

5. strstr()

기능. 서브스트링 찾기
str에서 find의 첫 번째 표시를 찾습니다. 함수는 일치 프로세스에서 find로 끝나는 널문자('\0')를 무시합니다.
원형. char* strstr(const char* str, const char* find)
리턴값. str에서 find의 첫번째 표시 시작 위치에 대한 포인터를 리턴합니다.
find가 str에 나타나지 않으면 NULL을 리턴합니다.
find가 길이가 0인 스트링을 가리키면, str을 리턴합니다.
예제.

char* str = "needle in a haystack here";
char* find = "haystack";
printf("%s", strstr(str, find)); //haystack here

6-1. strtok()

기능. 스트링 토큰화(tokenize)
지정된 문자를 기준으로 문자열을 자릅니다. strtok 함수를 사용할 때는 처음에만 자를 문자열을 넣어주고, 그 다음부터는 NULL을 넣어줍니다.
(알맞은 구분자를 찾으면 해당 구분자를 문장의 끝을 알리는 '\0'로 바꾸어 줍니다. 이후 또다시 strtok(NULL, "구분할 기준"); 함수를 호출하게 되면 이전에 찾은 구분자 뒤에서부터 다시 구분자를 찾게 됩니다. ==> NULL을 반환할 때까지 계속 불러줘야 합니다.)
원형. char* strtok(char* str, const char* delim)
매개변수1. char* str 자르고자 하는 문자열
매개변수2. const char* delim 자를 기준을 정하는 구분자
리턴값. 자른 문자열을 리턴합니다.
더 이상 자를 문자열이 없으면 NULL을 리턴합니다.
예제.

char str[] = "Michael Jordan B B";
char* temp = strtok(str, " ");
while (temp != NULL)
{
	puts(temp);
    temp = strtok(NULL, " ");
}

주의.
이 함수는 처음 인자를 수정하기 때문에 구분자의 원본을 잃습니다.
이 함수는 상수 문자열에서는 사용해서는 안 됩니다.
strtok() 함수는 파싱하는 동안 정적 버퍼를 사용하기 때문에 thread safe가 아닙니다.
문제가 된다면 strtok_r()을 사용해야 합니다.

6-2. strtok_r()

기능. strtok() 함수를 개선한 함수
인자값으로 자르고 남은 문자열을 저장하는 위치 saveptr의 주소를 받는다는 것을 제외하면 strtok() 함수와 같습니다.
원형. char* strtok_r(char* str, const char* delim, char** saveptr)
매개변수1. char* str 자르고자 하는 문자열
NULL이면 saveptr 변수에서 저장하고 있던 이전에 호출한 위치 다음부터 분리작업을 진행합니다.
매개변수2. const char* delim 자를 기준을 정하는 구분자
매개변수3. char** saveptr 분할한 문자열
리턴값. 정상적으로 분리하였으며, 분리된 문자열의 시작 pointer를 리턴합니다.
더 이상 자를 문자열이 없으면 NULL을 리턴합니다.
예제.

char str[] = "My Name is Dodo";
char* str2 = NULL;
char* temp = strtok(str, " ", &str2);
while (temp != NULL)
{
	puts(temp);
    puts(str2);
    temp = strtok(NULL, " ", &str2);
}

0개의 댓글