ch11 문자열 p.551~

문자열 함수 문제

C 라이브러리는 몇 개의 문자열 처리 함수들을 제공한다.

ANSI C는 그 함수들의 프로토타입들을 제공하기 위해 string.h 헤더 파일을 사용한다.

 

strlen()

  • 기능 : 문자열의 길이를 구한다.
  • 문제 17 : strlen()함수를 사용하여 긴 문자열을 단축시킨다.
/* 문자열을 단축시키는 함수 테스트 */

#include <stdio.h>
#include <string.h> /* 문자열 함수 프로토타입들이 들어 있다 */
void fit(char *, unsigned int);

int main(void)
{
    char mesg[] = "Things should be as simple as possible,"
    " but not simpler.";
    
    puts(mesg);
    fit(mesg,38);
    puts(mesg);
    puts("문자열의 나머지 부분을 보자");
    puts(mesg + 39);
    
    return 0;
}

void fit(char *string, unsigned int size)
{
    if (strlen(string) > size)
        string[size] = '\0';
}

  • fit() 함수는 배열의 39번째 원소에 들어 있는 콤마를 '\0'으로 대체한다.
  • puts()함수는 바로 그 첫 번째 널 문자에서 멈춘다. 그리고 배열의 나머지 부분은 무시한다.
  • 배열의 나머지 부분은 여전히 존재하므로 puts()함수에 표현식으로 주소를 전달한다.
  • 39번에 있는 스페이스 문자부터 널 문자를 만날 때까지 문자들을 출력한다.

 


 

strcat()

  • 기능
    • 두 개의 문자열을 전달인자로 사용하여 두 문자열을 결합한다.
    • 두 번째 문자열의 복사본이 첫 번째 문자열의 뒤에 덧붙는다.
    • 이처럼 결합된 문자열이 새로워진 첫 번째 문자열이 되고, 두 번째 문자열은 변경되지 않는다.
    • strcat()는 char *형이다. // char형을 가리키는 포인터
  • 리턴값
    • 첫 번째 전달인자의 값을 리턴한다. // 두 번째 문자열이 덧붙은 첫 번째 문자열의 첫 번째 문자의 주소
  • 문제 18 : strcat() 함수를 사용한다. 행을 읽을 때 fgets()를 사용하고, 개행 문자가 있다면 제거한다.
/* strcat() -- 두 문자를 결합한다. */
#include <stdio.h>
#include <string.h>  /* strcat() 함수를 선언한다 */
#define SIZE 80
char * s_gets(char * st, int n);
int main(void)
{
    char flower[SIZE];
    char addon[] =  "s smell like heaven.";
    
    puts("네가 가장 좋아하는 꽃이 뭐야?");
    if (s_gets(flower, SIZE))
    {
        strcat(flower, addon);
        puts(flower);
        puts(addon);
    }
    else
        puts("파일 끝 EOF!");
    puts("빠잉-*");


    return 0;
}


char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // must have words[i] == '\0'
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

  • 출력은 addon이 변경되지 않는 동안 flower는 변경된다는 것을 설명한다.

 


 

strncat()

  • 기능 
    • strcat() 함수는 두 번째 문자열이 첫 번째 배열에 맞는지 검사하지 않는다.
      • 첫 번째 배열에 공간을 충분히 할당하지 않으면, 배열을 채우고 남는 여분의 문자들이 인접한 메모리로 흘러넘치는 사태가 발생할 수 있다. 그러므로 strcat()을 사용하기 전에 strlen() 함수를 사용할 수 있다.
      • 널 문자를 위한 공간을 마련하기 위해 결합된 길이에 1을 더한다.
    • 다른 방법으로 strncat()을 사용한다. 이 함수는 추가할 자 개수의 최대값을 지정하기 위해 두 번째 전달인자를 사용한다. 예를 들면, strncat(bugs, addon, 13)addon 문자열의 내용을 bugs에 추가한다.
    • 추가하는 문자 개수가 13개에 도달하거나 널 문자를 만나면 추가를 멈춘다. 둘 중 먼저 일어나는 것을 수행한다.
    • (두 경우 모두 추가되는) 널 문자를 카운트하기 때문에, bugs 배열은 '최초의 문자열(널 문자 빼고)+추가되는 문자 길이+종결 널 문자'를 충분히 저장할 수 있을 만큼 커야한다.
  • 문제 19 : strncat()을 사용하여 available 변수를 위한 값을 계산한다. 허용되는 추가 문자들의 최대 개수로 이 값이 사용된다. 
/* 두 문자를 결합한다, 크기를 먼저 검사한다 */

#include <stdio.h>
#include <string.h>
#define SIZE 30
#define BUGSIZE 13

char * s_gets(char * st, int n);

int main(void)
{
    char flower[SIZE];
    char addon[] = "s smell like heaven.";
    char bug[BUGSIZE];
    int available;
    
    puts("네가 가장 좋아하는 꽃이 뭐야?");
    s_gets(flower, SIZE);
    if ((strlen(addon) + strlen(flower) + 1) <= SIZE)
        strcat(flower, addon);
    puts(flower);
    puts("네가 가장 싫어하는 벌레는 뭐야?");
    s_gets(bug, BUGSIZE);
    available = BUGSIZE - strlen(bug) - 1;
    strncat(bug, addon, available);
    puts(bug);
    
    return 0;
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // 반드시 '\0'이어야 한다
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

  • get()처럼, strcat()도 버퍼 오버플로우를 일으킨다.
  • strcat()을 버리고 strncat()만 사용하지 않는 이유는 gets()가 프로그램을 사용하는 것들로부터 위험을 노출시키고, strcat()은 부주의한 프로그래머들로부터 위험에 프로그램을 노출시키기 때문이다. 프로그래머들에 대한 신뢰성의 C철학에는 사용자가 strcat()를 언제 안전하게 사용할 수 있는지 알려줄 책임도 뒤따른다.

 


 

strcmp()

  • 문제 20 : 누군가의 응답을 저장되어 있는 문자열과 비교할 때 다음과 같은 프로그램을 사용할 수 있다.
/* 바르게 동작하는 지 확인 */

#include <stdio.h>

#define ANSWER "Grant"
#define SIZE 40

char * s_gets(char * st, int n);

int main(void)
{
    char try[SIZE];
    
    puts("Grant의 무덤에 누가 잠들어 있습니까?");
    s_gets(try, SIZE);
    while (try != ANSWER)
    {
        puts("틀렸습니다. 다시 말해 보세요.");
        s_gets(try, SIZE);
    }
    puts("정답입니다!");
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // must have words[i] == '\0'
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

  • 이 프로그램은 겉보기에는 그럴듯하지만 바르게 동작하지 않는다.
  • ANSWER와 try는 실제로는 포인터이므로 try != ANSWER 비교는 두 문자열이 같은지 검사하지 않고 두 문자열의 주소가 같은지 검사한다. 두 주소는 결코 같지 않으므로 사용자는 틀렸다는 대답만 끊임없이 듣게 된다.

  • 여기서 필요한 것은 문자열 주소가 아니라 문자열 내용을 비교하는 함수인데, strcmp()함수로 그 일을 수행할 수 있다.
    • 이 함수는 관계 연산자들이 수를 대상으로 수행하는 일을 문자열을 대상으로 수행한다.
    • 두 문자열 전달인자가 같으면 0을, 같지 않으면 0이 아닌 숫자를 리턴한다.
  • 문제 21 : 수정된 프로그램이다.
/* 20 수정 버전, 바르게 동작함! */

#include <stdio.h>
#include <string.h>   // strcmp()를 선언한다

#define ANSWER "Grant"
#define SIZE 40
char * s_gets(char * st, int n);

int main(void)
{
    char try[SIZE];
    
    puts("Grant의 무덤에 누가 잠들어 있습니까?");
    s_gets(try, SIZE);
    while (strcmp(try,ANSWER) != 0)
    {
        puts("틀렸습니다. 다시 말해 보세요.");
        s_gets(try, SIZE);
    }
    puts("정답입니다!");
        
    return 0;
}


char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // must have words[i] == '\0'
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

 

while (strcmp(try,ANSWER) != 0)                          // 0이 아닌 모든 값이 "True"이므로,
{                                                                             // While(strcmp(try,ANSWER))로 줄여서 사용할 수 있다.
    puts("틀렸습니다. 다시 말해 보세요.");
    s_gets(try, SIZE);
}

  • strcmp()의 기능 중 하나는, 배열들이 아니라 문자열들을 비교하는 것이다.
  • 배열 try가 40개의 메모리 셀을 가지고, "Grant"가 널 문자 셀을 포함하여 6개의 메모리 셀을 가지지만, 첫 번째 널 문자까지의 try 부분을 기준으로 비교가 이루어진다. 따라서 strcmp()는 서로 다른 크기의 배열에 저장되어 있는 문자열들을 비교하는 데에도 사용할 수 있다.

 

① strcmp()의 리턴값

  • 문제 22 : 비교하는 문자열들이 같지 않으면 strcmp()는 어떤 값을 리턴하는 지 확인한다.
/* strcmp()의 리턴값 */

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

int main(void)
{
    
    printf("strcmp(\"A\", \"A\") is ");
    printf("%d\n", strcmp("A", "A"));
    
    printf("strcmp(\"A\", \"B\") is ");
    printf("%d\n", strcmp("A", "B"));
    
    printf("strcmp(\"B\", \"A\") is ");
    printf("%d\n", strcmp("B", "A"));
    
    printf("strcmp(\"C\", \"A\") is ");
    printf("%d\n", strcmp("C", "A"));
    
    printf("strcmp(\"Z\", \"a\") is ");
    printf("%d\n", strcmp("Z", "a"));
    
    printf("strcmp(\"apples\", \"apple\") is ");
    printf("%d\n", strcmp("apples", "apple"));
    
    return 0;
}

  • ANSI 표준은 strcmp()가 첫 번째 문자열이 두 번째 문자열보다 알파벳 순서로 앞에 오면 음수를, 뒤에 오면 양수를 리턴한다고 정의한다. 그러나 정확한 수치 값은 각 컴파일러에 위임되어 있다.(일부 다른 시스템에서는 ASCII 코드값의 차인 2를 리턴할 수도 있다.)
  • 다음은 문자 코드값들의 차를 리턴하는 또 다른 시스템에서 얻은 출력 내용이다.
strcmp("A", "A")은 0
strcmp("A", "B")은 -1
strcmp("B", "A")은 1
strcmp("C", "A")은 =2
strcmp("Z", "a")은 -7
strcmp("apples", "apple")은 115
  • 일반적으로 strcmp()는 일치하지 않는 첫 문자 쌍이 나올 때까지 문자 쌍을 차례로 비교한다. 일치하지 않는 첫 문자 쌍이 나타나면 해당하는 값을 리턴한다.
  • "apples", "apple"은 첫 번째 문자열부터 마지막 문자인 s 앞까지 일치한다. 문자 s는 두 번째 문자열 "apple"의 여섯 번째 문자인 널 문자(ASCII 0)에 대응한다. 널 문자는 ASCII 시퀀스에서 첫 번째 문자이므로, s보다 앞에온다. 그래서 strcmp()는 양수를 리턴한다.
  • 이 비교에서 strcmp()가 글자뿐만 아니라 모든 문자들을 비교한다는 것을 알 수 있다. 알파벳 순서로 비교가 이루어진다고 말하는 대신에 기계 조회 문자들이 그들의 수치값(대표적으로 ASCII 값)으로 비교된다는 것을 의미한다.
  • ASCII 코드에서 대문자들의 코드가 소문자들의 코드보다 앞에 온다. 그래서 strcmp("Z", "a")의 리턴 값은 음수이다.
  • 문자열들을 알파벳 순서로 소팅하고자 할 때에는 리턴값이 양수인지, 0인지, 음수인지 알아야 하지만 대부분의 경우에 리턴값의 정확한 수치는 그다지 중요하지 않다. 0인지 아닌지, 즉 일치 여부만 알면 된다.

② strcmp() 문자열 비교

  • strcmp() 함수는 문자들(characters)이 아닌 문자열들(strings)을 비교하는 함수이다. 
    • "apples"와 "A" 같은 전달인자는 사용할 수 있지만, 'A'와 같은 문자 전달 인자는 사용할 수 없다.
    • 그러나 char형은 정수형이기 때문에, 문자들을 비교하는 데에는 관계 연산자를 사용할 수 있다.
    • word가 char형 배열에 저장되어 있는 문자열이고, ch가 char형 변수라고 가정할 때, 다음 문장은 유효하다.
      ※ strcmp()의 전달인자로 ch나 'q'를 사용하는 것은 안된다!
      if (strcmp(word, "quit") == 0)    // 문자열 비교에 strcmp()를 사용한다.
          puts("끝!"); 
      if (ch == 'q')                              // 문자 비교에 ==를 사용한다.
          puts("끝!")
  • 문제 23 : 프로그램이 입력 읽기를 멈추어야 하는지 검사하기 위해 strcmp()를 사용한다.
/* 어떤 프로그램의 시작 부분 */

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

#define SIZE 80
#define LIM 10
#define STOP "quit"

char * s_gets(char * st, int n);

int main(void)
{
    char input[LIM][SIZE];
    int ct = 0;
    
    printf("%d라인까지 입력하십시오 (끝내려면 quit):\n", LIM);
    while (ct < LIM && s_gets(input[ct], SIZE) != NULL &&
           strcmp(input[ct],STOP) != 0)
    {
        ct++;
    }
    printf("%d개의 문자열이 입력되었습니다.\n", ct);
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // 반드시 '\0'이어야 한다.
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

  • EOF 문자를 만났을 때(이 프로그램의 경우 gets()가 널 포인터를 리턴했을 때), 사용자가 단어 quit를 입력했을 때, 사용자가 입력 한계 LIM에 도달했을 때 읽기를 멈춘다.
  • 빈 라인(아무 것도 입력하지 않고 엔터키만 누른 경우)을 입력함으로써 읽기를 멈추는 것이 간혹 편리할 때가 있다. 이를 위해, while 루프 제어문을 다음과 같이 수정할 수 있다.

    while (ct < LIM && gets (input[ct]) != NULL && input[ct][0] != '0')
  • 여기서, input[ct]는 방금 입력된 문자열이고, input[ct][0]은 그 문자열의 첫 번째 문자다.
  • 사용자가 빈 라인을 입력하면, gets()은 첫 번째 원소에 널 문자를 넣는다. 그래서 input[ct][0] != '\0'같은 표현식으로 사용자가 빈 라인을 입력했는지 검사할 수 있다.

 


 

strncmp()

  • 기능 : 대응하는 두 문자가 일치하지 않는 문자 쌍을 찾을 때까지 문자열들을 비교한다.
    • 이것은 둘 중 한 문자열의 끝에 도달할 때까지 검색이 이루어질 수 있다는 것을 의미한다.
    • strncmp() 함수는 일치하지 않는 문자 쌍을 만날 때까지 또는 세 번째 전달인자로 지정한 문자들의 개수까지만 문자열들을 비교한다.
  • 문제 24 : "astro"로 시작하는 문자열들을 검색하고자 할 때, 처음 5개의 문자만 검색하도록 제한할 수 있다.
/* strncmp()를 사용한다 */

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

#define LISTSIZE 6

int main()
{
    const char * list[LISTSIZE] =
    {
        "astronomy", "astounding",
        "astrophysics", "ostracize",
        "asterism", "astrophobia"
    };
    int count = 0;
    int i;
    
    for (i = 0; i < LISTSIZE; i++)
        if (strncmp(list[i],"astro", 5) == 0)
        {
            printf("찾았습니다 : %s\n", list[i]);
            count++;
        }
    printf("astro로 시작하는 단어를 %d개"
           " 찾았다.\n", count);
    
    return 0;
}

 


strcpy()와 strncpy() 함수

 

1. strcpy() 

  • pts1과 pts2가 둘 다 문자열을 가리키는 포인터일 때, 표현식 pts1 = pts2;는 문자열 자체가 아니라 문자열의 주소만을 복사한다. 
  • 문자열을 복사하기를 원할 때, strcpy() 함수를 사용할 수 있다. 
  • 문제 25 : 사용자에게 q로 시작하는 단어를 입력하라고 요구한다. 프로그램은 입력을 임시 배열에 복사하고, 첫 번째 문자가 q이면 strcpy() 함수를 사용하여 그 문자열을 임시 배열에서 영구적인 장소로 복사한다. strcpy() 함수는 대입 연산자의 역할을 문자열에 대해 수행한다.
/* strcpy()를 사용한다 */

#include <stdio.h>
#include <string.h>  // strcpy() 선언
#define SIZE 40
#define LIM 5

char * s_gets(char * st, int n);

int main(void)
{
    char qwords[LIM][SIZE];
    char temp[SIZE];
    int i = 0;
    
    printf("q로 시작하는 단어를 %d개 입력하세요 : \n", LIM);
    while (i < LIM && s_gets(temp, SIZE))
    {
        if (temp[0] != 'q')
            printf("%s : q로 시작하는 단어가 아닙니다. \n", temp);
        else
        {
            strcpy(qwords[i], temp);
            i++;
        }
    }
    puts("받아들인 단어들은 다음과 같습니다 :");
    for (i = 0; i < LIM; i++)
        puts(qwords[i]);
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // 반드시 '\0' 이어야 한다.
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

 

    while (i < LIM && s_gets(temp, SIZE))
    {
            if (temp[0] != 'q')      ─
                printf("%s : q로 시작하는 단어가 아닙니다. \n", temp);      
            else      ─
            {
                strcpy(qwords[i], temp);      ─ ③
                i++;      ─
            }
    }

  • ①  temp 배열의 첫 번째 문자가 q가 아닌지 검사한다. 
    • 문자열 기반의 검사를 사용할 수도 있다. 
      if (strncmp(temp, "q", 1) != 0)
    •  문자열 temp와 "q"의 첫 번째 원소가 서로 다른지 검사한다.
  • ② ④ 입력한 단어가 q 검사를 통과하는 경우에만 카운터 i가 증가한다.
  • ③ 두 번째 전달인자(temp)가 가리키는 문자열이 첫 번째 전달인자 qwords[i]가 가리키는 배열로 복사된다.
    • 복사본을 타깃(target)이라고 하고, 원래의 문자열을 소스(source)라고 한다.
    • 전달인자의 순서는 대입문에서의 순서와 같다. 타깃 문자열은 왼쪽에, 소스 문자열은 오른쪽에 있다.)
  • 타깃 배열이 소스 배열을 복사할 수 있을 만큼 충분한 기억 공간을 가지는지 확인하는 것은 프로그래머의 몫이다.

    char* str;

    int x ;
    x = 50;                      // 수를 대입한다
    strcpy(target, "Hi!");  // 문자열을 대입한다
    target = "So long";    // 신택스 에러
  • 다음과 같이 하면 문자열 "The C of Tranquility"를 str이 지정하는 주소에 복사된다.
    그러나 str이 초기화되지 않았기 때문에 어디로 복사될 지 알 수 없다.


    char* str;

    strcpy(str, "The C of Tranquility"); // 문제가 발생한다.

 

strcpy()는 두 개의 문자열 포인터를 전달인자로 사용한다.

타깃 문자열을 가리키는 첫 번째 포인터는, 그 문자열을 저장할 수 있을 만큼 충분한 기억 공간을 확보하는, 배열과 같은 객체를 가리켜야 한다.
선언된 포인터, 배열 이름, 문자열 상수가 소스 문자열을 가리키는 두 번째 포인터가 될 수 있다.

배열을 선언하면 데이터를 위한 기억 공간이 할당되고, 포인터를 선언하면 하나의 주소를 위한 기억 공간이 할당된다.

  • strcpy() 함수는 두 가지 유용한 특성을 더 가진다.
    1. strcpy()는 char *형이다. strcpy()는 첫 번째 전달인자의 값(=문자의 주소)을 리턴한다.
    2. 첫 번째 전달인자가 반드시 배열의 시작을 가리킬 필요는 없다. 이것은 배열의 일부만 복사할 수도 있다는 것을 의미한다.
  • 문제 26 : strcpy()의 두 가지 특성을 함께 보여준다.
/* strcpy()를 사용한다 */

#include <stdio.h>
#include <string.h>    // strcpy()를 선언한다

#define WORDS  "beast"
#define SIZE 40

int main(void)
{
    const char * orig = WORDS;
    char copy[SIZE] = "Be the best that you can be.";
    char * ps;
    
    puts(orig);
    puts(copy);
    ps = strcpy(copy + 7, orig);
    puts(copy);
    puts(ps);
    
    return 0;
}

▲ strcpy() 함수는 포인터들을 사용한다.

  • strcpy() 함수는 소스 문자열로부터 널 문자를 복사한다.
  • 이 예제에서 널 문자는 copy 배열에 들어있는 that의 첫 문자 t 위치에 쓰여지므로, 새로운 문자열은 beast로 끝난다.
  • 첫 번째 전달인자가 copy+7이기 때문에 ps는 copy의 8번째 원소(인덱스 7)을 가리킨다.
  • puts(ps)는 그 위치에서 시작하여 문자열을 출력한다.

2. strncpy()

/* strncpy()를 사용한다 */

#include <stdio.h>
#include <string.h>  /* strncpy()를 선언한다 */

#define SIZE 40
#define TARGSIZE 7
#define LIM 5

char * s_gets(char * st, int n);

int main(void)
{
    char qwords[LIM][TARGSIZE];
    char temp[SIZE];
    int i = 0;
    
    printf("q로 시작하는 단어를 %d개 입력하세요 : \n", LIM);
    while (i < LIM && s_gets(temp, SIZE))
    {
        if (temp[0] != 'q')
            printf("%s : q로 시작하는 단어가 아닙니다!\n", temp);
        else
        {
            strncpy(qwords[i], temp, TARGSIZE - 1);
            qwords[i][TARGSIZE - 1] = '\0';
            i++;
        }
    }
    puts("받아들인 단어들은 다음과 같습니다 : ");
    for (i = 0; i < LIM; i++)
        puts(qwords[i]);
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // '\0'이어야 한다
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

 

 strncpy(qwords[i], temp, TARGSIZE - 1);
            qwords[i][TARGSIZE - 1] = '\0';

 

  • strncpy() 함수 호출은 n개의 문자들까지 또는 널 문자를 만날때까지 source에서 target으로 복사한다.
  • source에 있는 문자들의 개수가 n보다 작으면, 널 문자를 포함하여 전체 문자열이 복사된다.
  • 그 함수는 n개의 문자들보다 결코 더 많이 복사하지 않는다.
    • 소스 문자열의 끝에 도달하기도 전에 복사 한계에 도달하면, 널 문자가 추가되지 않는다.
    • 최종 결과물은 널 문자를 포함하는 경우도 있고, 포함하지 않는 경우도 있다.
  • 이 이유로 프로그램은 n을, 타깃 배열의 크기보다 하나 작은 값으로 설정한다.
  • 그러고 나서 배열의 최종 원소를 널 문자로 설정한다.
  • 이것은 문자열이 저장되도록 보증한다.
    • 소스 문자열이 타깃에 실제로 맞는다면, 소스 문자열과 함께 복사된 널 문자가 문자열의 실제 끝을 표시한다.
    • 소스 문자열이 타깃에 맞지 않는다면 이 최종 널 문자가 문자열의 끝을 표시한다.

sprintf()

  • sprintf() 함수는 string.h가 아니라 stdio.h에 선언되어 있다.
  • printf()와 비슷하게 동작하지만, 디스플레이에 출력하는 대신에 문자열에 출력한다.
  • 이 함수는 여러 항목들을 하나의 문자열로 결합하는 방법을 제공한다.
  • sprintf()의 첫 번째 전달인자는 타깃 문자열의 주소이다. 나머지 전달인자들은 printf()와 동일하게 포맷 문자열이 먼저 나오고, 출력할 항목들의 리스트가 뒤따른다.
  • 문제 28 : sprintf()를 사용하여 세 개의 항목을 하나의 문자열로 결합한다. 
/* 문자열을 포맷 지정한다 */

#include <stdio.h>
#define MAX 20

char * s_gets(char * st, int n);

int main(void)
{
    char first[MAX];
    char last[MAX];
    char formal[2 * MAX + 10];
    double prize;
    
    puts("이름을 입력하세요 : ");
    s_gets(first, MAX);
    puts("성을 입력하세요 : ");
    s_gets(last, MAX);
    puts("상금을 입력하세요 : ");
    scanf("%lf", &prize);
    sprintf(formal, "%s, %-19s: $%6.2f\n", last, first, prize);
    puts(formal);
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    int i = 0;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // 반드시 '\0'이어야 한다
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

 

sprintf(formal, "%s, %-19s: $%6.2f\n", last, first, prize);

  • sprintf()문은 입력을 하나의 표준 형식으로 포맷하여 문자열 formal에 저장한다.

 


cpy : copy : 복사해 함수

 

strcpy(대상 문자열, 원본 문자열);

  • char *strcpy(char * restrict s1, const char * restrict s2);
    • s2가 가리키는 문자열(널 문자 포함)을 s1이 가리키는 위치에 복사한다.
    • 리턴값 : s1

strncpy(대상 문자열, 원본 문자열, 복사할 문자 수)

  • char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
    • s2가 가리키는 문자열로부터 복사 한계 n까지 문자들을 s1이 가리키는 위치에 복사한다.
    • 널 문자 이후의 문자들은 복사되지 않는다.
    • 소스 문자열이 n보다 짧을 경우, 타깃 문자열의 나머지 부분은 널 문자로 채워진다.
    • 소스 문자열이 n이거나 그보다 길 경우, 널 문자는 복사되지 않는다.
    • 리턴값 : s1
strcpy(), strncpy() 함수는 현대의 프로그래밍에서는 보안상의 이슈가 있어서 더 이상 사용하지 않..는...다..고...한..다..ㅋ^^

 

// size_t는 unsigned int 이므로 0보다 큰 값을 줘야 한다. 그렇지 않으면 언더플로우 발생하여 문자 끝까지 비교한다. (이하 동일)


cat : concatenate 붙여 함수

// n이 붙으면 n번 붙여줘 /  l 이 붙으면  n-1번 붙여줘 

 

strcat(문자열1, 문자열2)

  • char *strcat(char * restrict s1, const char * restrict s2);
    • s2가 가리키는 문자열을 s1이 가리키는 문자열의 끝에 복사한다.
      1. s2 문자열의 첫 번째 문자는 s1 문자열의 널 문자 위치에 쓰여진다.
      2. s2 문자열에 있는 널 문자와 그 이후의 문자는 복사되지 않는다.
      3. 널 문자가 결합된 결과에 추가된다.
    • s1의 크기가 s2를 이어 붙일 정도의 충분한 크기여야 한다. (s1+s2) > s1 크기-1 이면 실행 오류!!!
    • 리턴값 : s1

strncat(문자열1, 문자열2, count)

  • char *strncat(char* restrict s1, const char* restrict s2, size_t n);
    • s2 문자열의 처음 n개까지의 문자들을 s1 문자열의 끝에 덧붙인다.
      1. s2 문자열의 첫 번째 문자는 s1 문자열의 널 문자 위치에 쓰여진다.
      2. s2 문자열의 처음 n개까지만 복사된다.
      3. 널 문자가 결합된 결과에 추가된다.
    • s1의 크기가 s2를 이어 붙일 정도의 충분한 크기여야 한다. (s1+s2) > s1 크기-1 이면 실행 오류!!!
    • 리턴값 : s1

strlcat(문자열1, 문자열2, size)

  • 여기서 size는 두 개의 문자열이 합쳐질 공간의 크기를 의미한다. 미리 정해서 넘겨준다.
    • Q. 문자열1의 길이가 size보다 크면 어케 되나여?
      A. 암것도 안함 수행 안함 걍 안함
    • Q. 문자열1의 길이가 size보다 작으면 어케 되나요?
      A. 문자열1은 다 들어가고 남은 공간-1(널)만큼만 문자열2를 붙인다. 
    • 리턴값 : 항상 두 문자열이 연결된 정확한 길이를 반환하지 않는다. 
      • ① 문자열1 < size   :  실제 두 문자열이 잘림없이 정상적으로 합쳐질 때의 길이
      • ② 문자열1 > size . :  문자열2 길이+size    (대체왜? 몰루)
      • ③ 문자열1 == size :  문자열2 길이+size    (......몰루)
      • 문자열1 > size일 때, ①이 반환됐다는 것은 무조건 (문자열2+size) < (문자열1+문자열2) 상황이라는 걸  알면 될듯!

cmp : compare : 비교해 함수

 

strcmp(문자열1, 문자열2)

  • int strcmp(const char* s1, const char* s2);
    • s1 문자열이 s2 문자열보다 기계 조회 순서로 뒤에 오면 양수를, 동일하면 0, 앞에 오면 음수를 리턴한다.
    • 리턴값 : 양수 또는 음수 또는 0

 

strncmp(문자열1, 문자열2, count)

  • int strcmp(const char* s1, const char* s2, size_t n);
    • n개의 문자들까지만 또는 첫 번째 널 문자를 만날 때까지만 비교한다. 둘 중 먼저 일어나는 것을 수행한다.
    • s1 문자열이 s2 문자열보다 기계 조회 순서로 뒤에 오면 양수를, 동일하면 0, 앞에 오면 음수를 리턴한다.
    • 리턴값 : 양수 또는 음수 또는 0

위치 찾아 함수

// r이 붙으면 뒤부터 찾아줘

 

strchr(const char* 문자열, int 찾을 대상)

  • Find character(s) in the string
const char * strchr ( const char * str, int character );
char * strchr ( char * str, int character );
  • 문자열에서 찾고자 하는 대상을 문자열 범위에서 검색하고, 처음 matching이 발생하면 그것의 절대 메모리 주소를 반환하거나 널 문자를 만나면 함수가 종료된다.
    • 종결 널 문자는 문자열의 일부이므로 찾는 대상이 될 수 있다.
    • 탐색이 실패하면 널 포인터를 리턴한다.
  • 문자열만 전용으로 한다는 특징과 문자열 내 일정 바이트만 검사하는 옵션이 아니라 문자열 전체를 순회하면서 결과를 찾는다는 특징이 있다.

strrchr(const char* 문자열, int 찾을 대상)

  • Find character(s) in the string in reverse
char * strrchr ( const char *, int );
  • 문자열에서 찾고자 하는 대상을 문자열 뒤부터 탐색을 시작해서 처음 matching이 발생하면 그것의 절대 메모리 주소를 반환하거나 널 문자를 만나면 함수가 종료된다.
    • 처음 matching이 발생한 그것은 문자열에서 마지막으로 나타나는 위치라고 할 수 있다.
    • 종결 널 문자는 문자열의 일부이므로 찾는 대상이 될 수 있다.
    • 탐색이 실패하면 널 포인터를 리턴한다.
  • 문자열만 전용으로 한다는 특징과 문자열 내 일정 바이트만 검사하는 옵션이 아니라 문자열 전체를 순회하면서 결과를 찾는다는 특징이 있다.

strspn(문자열1, 문자열2)

  • string match span
#include <string.h>  

size_t strspn(const char* str1, const char* str2);
  • 문자열2(str2)의 문자만을 포함하는 문자열1(str1)의 처음 부분의 길이를 구한다.
  • str1의 맨 처음 부터 문자들을 비교해서, str2에 있는 문자들만을 포함한 곳까지의 길이를 반환한다. 
    • str1이 "ABCD123"이고, str2가 "AB123" 이면, strspn() 함수의 결과는 2
    • str1이 "KABCD123"이고, str2가 "AB123"이면, str1의 첫 시작 문자 'K'가 str2 어디에도 들어가있지 않기 때문에, 그 뒤에 matching case가 존재함에도 불구하고 strspn() 함수의 결과는 0이다.
    • str1에 "321ab", str2에 "12346"가 있다면 3,2,1이 해당하므로 3이 리턴된다. 
    • str1에 "ab321"이었다면 리턴값이 0이다.
  • 리턴값 : str1에서 오직 str2에 포함되어 있는 문자들만이 있는 부분의 길이
                 // 만일 str1의 첫 번째 문자가 str2에 없다면 리턴값은 0이다.

숫자 야구 스트라이크 판정 느낌?????????????은 내생각

strcspn(문자열1, 문자열2)

  • string with break pointer 
#include <string.h>  

size_t strspn(const char* str1, const char* str2);
  • 문자열1에서 문자열2에 포함되어 있는 문자들을 검색하되, 첫번째로 일치하는 문자에 도달하기 까지 읽어들인 문자의 개수를 리턴한다.
    • str1에 "Hello, World", str2에 "aeiou"가 있다면 'e'가 일치하므로 'e'까지 읽어들이는데 읽은 문자 수, 1을 리턴한다.
  • 리턴값 : 첫번째로 일치하는 문자에 도달하기 까지 읽어들인 문자의 개수

strpbrk(문자열1, 문자열2)

  • string with break pointer 
const char * strpbrk ( const char * str1, const char * str2 );      
char * strpbrk ( char * str1, const char * str2 );
  • 주어진 문자열(문자열1)을 처음부터 끝까지 검색하면서 비교 대상(문자열2)을 찾다가 첫 번째로 matching되는 대상의 절대 메모리 주소를 반환한다. 
  • 상대적인 offset을 반환하는 strcspn() 함수와는 대조적이다. 따라서, 상대적 위치를 나타내기 위해서는 (match)-(sring)+1형태로 계산해 주어야 한다.
    char* match = strpbrk(string, number);
    printf("A first occurance of number in the string found at %ldth position.\n", match - string + 1); 

strstr(const 문자열1, const 문자열2)

  • Find string in the given string
#include <string.h>  

size_t strspn(const char* str1, const char* str2);
  • 문자열1(str1) 에서 문자열2(str2) 를 검색하여 가장 먼저 나타나는 곳의 위치를 포인터 형태, 즉 절대 메모리 주소로 리턴한다. 이 때 일치하는 문자열이 없다면 널 포인터를 리턴하게 된다.
  • 검색에서 마지막 널 문자는 포함하지 않는다.
  • 리턴값 : str1에서 str2를 찾으면 가장 먼저 나오는 곳의 위치를 리턴한다. str2 str1에 존재하지 않는다면 널을 리턴한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'C언어 > 스터디' 카테고리의 다른 글

ch14 구조체와 그 밖의 데이터형  (1) 2024.08.13
ch11 문자열과 문자열 함수  (0) 2024.08.13
ch10 배열과 포인터  (0) 2024.08.11
ch09 함수  (0) 2024.08.10
ch7 제어문_분기와 점프  (0) 2024.08.09