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;
}
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;
}
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을 리턴함
}
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;
}
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;
}
- 과목명 입력받아 공백 제거하기
- 콤마로 분할하기
- 존재하는 과목명인지 체크하기
- 분할된거 양식 맞춰 이어붙이기
#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;
}
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*/
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));
strlen()
기능. 종료 널문자(
\0
)를 제외하고 string 길이를 판별합니다.
원형.size_t strlen(const char* string)
매개변수.const char* string
리턴값.size_t
문자열 길이
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보다 작은 값을 리턴합니다.
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 이므로 양수 반환
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;
strncasecmp()
기능. 대소문자를 구분하지 않고 길이를 지정해서 두 문자열을 비교합니다.
원형.int strncasecmp(const char* str1, const char* str2, size_t n)
리턴값. str1과 str2가 일치하면 0을 리턴합니다.
str1이 str2보다 크다면 0보다 큰 값을 리턴합니다.
str1이 str2보다 작다면 0보다 작은 값을 리턴합니다.
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"
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"
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
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'
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
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()
을 사용해야 합니다.
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); }