brunch

매거진 Sinclair

You can make anything
by writing

C.S.Lewis

by Sinclair Jul 29. 2016

문자열 이야기

프로그램과 우리 사이, 대화가 필요해 III



문자와 문자열 처리 함수들 2   



찾아라!!

문자열에서 내가 원하는 것을 찾아주는 다양하고 고마운 함수들                              


char * strchr(const char * s, int c);

char * strrchr(const char * s, int c);


문자열에서 지정한 문자의 위치를 찾아 그 문자의 주소 값을 리턴 하는 함수들입니다. strchr() 함수는 문자열의 시작부터 검색하고 strrchr() 함수는 문자열의 끝부터 검색을 합니다. 그렇기 때문에 strchr("Sinclair",'i') 라고 하면 "Sinclair"문자열의 시작에서부터 처음 나온 i의 위치인 "Sinclair" + 1 번지의 주소 값이 나오고 strrchr("Sinclair" ,'i') 라고 하면 "Sinclair"문자열의 끝에서부터 처음 나온 i의 위치인 "Sinclair" + 6 번지의 주소 값이 나옵니다. 찾을 수 없다면 모두 널 포인터를 리턴 합니다.       

  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/   

// prototypes

int strlen2(register const char *) ;   

char * strchr2(register const char * , int) ; // while 사용

char * strchr3(register const char * , int) ; // for 사용

char * strrchr2(register const char * , int) ;      


char * strchr2(register char * s , int c) { // while 사용   

    register leng = strlen(s)-1 ;   

    while( *s++ ^ c ) { if( ! leng-- ) return (void*)0 ; }   

    return --s ;   

} // end strchr2()      


char * strchr3(register const char * s , int c) { // for 사용   

    register leng = strlen(s) ;      

    for ( ; leng-- ; ) { if( ! (*s++ ^ c) ) return --s ; }   

    return (void*)0 ;   

} // end strchr3()      


char * strrchr2(register const char * s , int c) {   

    register leng = strlen(s) - 1 ;   

    s += leng ;

    while( *s-- ^ c ) { if( ! leng-- ) return (void*)0 ; }   

    return ++s ;   

} // end strrchr2()                                       



size_t strcspn(const char * s1, const char * s2);

size_t strspn(const char * s1, const char * s2);


문자열 s2의 모든 문자들을 문자열 s1의 각 문자들을 하나씩 검사하여 인덱스, 즉 시작번지로부터 얼마만큼 떨어져 있는가에 대한 위치를 알려주는 함수입니다. strcspn()함수는 s2 문자열의 문자들 중 등장하는 처음 위치를, 반대로 strspn()함수는 등장하지 않는 처음 위치를 알려줍니다.   


    printf("%d\n" , strcspn("Sinclair" , "air")) ; // == 1

    // 문자열"Sinclair"에서 'a','i','r'이 처음 나온 곳, 'i'의 위치

    printf("%d\n" , strspn("Sinclair" , "Sir")) ; // == 2

    // 문자열"Sinclair"에서 'S','i','r'이 아닌 문자가 처음 나온 곳,
    // 'n'의 위치      

    printf("%d\n" , strcspn("Sinclair" , "xyz")) ; // == 8

    // s1의 모든 문자들에서 등장하지 않을 경우 strlen(s1)값이 리턴

    printf("%d\n" , strspn("Sinclair" , "Sinclar")) ; // == 8

    // s1의 모든 문자들에서 등장할 경우 strlen(s1)값이 리턴      

    printf("%d\n" , strcspn("Sinclair" , "Sinclar")) ; // == 0

    // s1의 모든 문자들에서 등장할 경우 당연히 0값이 리턴

    printf("%d\n" , strspn("Sinclair" , "xyz")) ; // == 0

    // s1의 모든 문자들에서 등장하지 않을 경우 당연히 0값이 리턴        


                               

char * strpbrk(const char * s1, const char * s2);


strcspn()과 같은 역할을 합니다. 일반적으로 strcspn() 함수가 위치 인덱스를 리턴 해준다면 strpbrk() 함수는 그곳의 주소 값을 리턴 합니다. 그러므로 아래 등식이 성립하게 됩니다. 만약에 같은 문자를 찾을 수 없다면 널 포인터를 리턴 합니다.
   

strcspn("Sinclair","air") == strpbrk("Sinclair"," air") – "Sinclair"

"Sinclair" + strcspn("Sinclair","air") == strpbrk("Sinclair"," air")           


                            

char * strstr(const char * s1, const char * s2);

char * strtok(char * s1, const char * s2);


strcspn() 함수나 strpbrk() 함수가 각 문자들이 포함되어 있는지 알려주는 함수였다면 strstr() 함수는 첫 번째 문자열의 시작부터 검색을 통해 두 번째 문자열이 통째로 포함되어 있다면 처음 등장하는 그곳의 주소 값을 리턴 하는 함수입니다. 물론 찾지 못하게 되면 널 포인터를 리턴 합니다.



strtok() 함수는 첫 번째 문자열을 두 번째 문자열에 나오는 문자를 만나면 널 문자로 바꿔서 문자열 토큰을 만들어 주는 함수입니다. 첫 번째인자로 넣어주는 문자열이 변경 또는 파괴되기 때문에 중요한 문자열일 경우 strtok() 함수를 부르기 전에 미리 사본을 만들어 두어야 합니다. 그리고 상수 문자열을 첫 번째 인자로 넘겨주면 세그멘테이션 오류가 발생할 수도 있다는 것을 반드시 기억해야 합니다. ‪

만약 방금 전 문자열 토큰이 추출되고 남은 문자열을 가지고 다시 토큰을 추출해야 한다면 이번엔 첫번째 인자로 NULL을 넣어야합니다. 이것은 처음에 strtok 함수가 패턴에 따라 문자열을 분리 추출해 문자열 토큰을 만든 후에 문자열의 분리지점을 '\0'로 바꾼 후 그 바로 다음 문자의 주소값을 가지고 있기 때문입니다. 다시 말해 strtok()는 첫번째 인자로 특정 문자열의 주소값이 들어오면 그 문자열의 분리를 시도하지만 NULL이면 바로 전에 추출되고 남은 문자열의 분리를 시도하게 됩니다.‬


  

#include <stdio.h>

#include <string.h>   

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/      


int main() {   

    char pearl[] =  "The most extraordinary thing about the oyster is

this. Irritations get into his shell. He does not like them; he tries to get rid of them. But when he cannot get rid of them he settles down to make of them one of the most beautiful things in the world. He uses the irritation to do the loveliest thing that an oyster ever has a chance to do.\nIf there are irritations in our lives today, there is only one prescription: make a pearl. It may have to be a pearl of patience, but, anyhow, make a pearl. And it takes faith and love to do it.\n\n - Make A Pearl by Harry Emerson Fosdick" ;   


    char * word ;

    char * token = " ,.;:-\n" ;

    int c = 0 ;   

    printf("%s\n\n\n" , pearl ) ;  // 원문

    word = strtok(pearl , token) ;

    printf("%s\n\n\n" , pearl ) ; // == The <-  추출된 처음 토큰

    while(word) {

        printf("%s%c" , word , (++c % 7) ? '\t' : '\n') ;

        word = strtok(NULL , token) ;

        // 두 번째 토큰부터는 NULL을 인자로 넣어 남은 문자열에서 토큰을 추출해야 합니다.

    } // end while

    putchar('\n') ;   

    printf("%s\n\n\n" , pearl ) ; // == The <- 여전히 남아있는 처음 토큰      

    puts(strpbrk("Sinclairs" , "air")) ;

    // == inclairs

    puts("Sinclairs" + strcspn("Sinlciars" , "air")) ;

    // == inclairs

    puts(strstr("Sinclairs" , "air")) ;

    // == airs      

    return 0 ;   

} // end main()       



  

그 외에 아는 게 힘이 되는 멋진 떨거지(?) 함수들                              

char * strerror(int errcode);


세상이 창조되고 아담이 가장 먼저 한 일이 바로 모든 사물에 이름을 붙이는 것이었습니다. 그래서인지 사람들은 이 세상의 모든 것에 이름 붙이는 걸 좋아합니다. 그러나 일반적으로 OS(운영체제)는 번호 붙이는 걸 좋아하기 때문에 OS(운영체제)는 발생할 수 있는 모든 에러에 대해서 번호를 붙여놓고 그 지정 번호로 알려줍니다. 그 번호를 인자로 넣어주면 지정 번호에 해당하는 에러 메시지로 바꿔주는 함수가 바로 strerror() 함수 입니다. 0번 에러는 바로 실행 성공이며 -1번 에러가 일반적으로 unknown 에러입니다.  


                                  

char * strdup(const char * s);


이미 알고 있는지 모르겠지만 이 함수는 안에 malloc() 함수가 들어 있습니다. 때문에 사용 후에 free() 함수 호출을 안 하면 문제가 발생할 수도 있습니다. strdup() 함수가 비 표준 함수 임에도 이것을 설명하게 된 이유는 strcpy() 함수가 지닌 문제점을 보완한 썩 괜찮은 함수이기 때문입니다. 그래서 대부분의 컴파일러가 제공하고 있기는 하지만 만약에 컴파일러가 지원하지 않는다면 직접 만들어 사용할 수 있어야 합니다. 다음 글타래에서 malloc() 함수를 설명하고 나서 직접 만들고 사용해 보겠습니다.                                    



sprintf(char * s , const char * fmt , ... );

sscanf(const char * s , const char * fmt , ... );


printf() 와 scanf() 함수들은 buffer로 입출력을 하는데 sprintf() 함수와 sscanf() 함수는 지정 문자열로 입출력을 합니다. 사용 방법은 첫 번째 인자로 메모리 할당이 된 문자열의 시작 주소를 사용하는 것을 제외하면 printf()나 scanf() 함수를 사용하는 것과 같습니다.

컴퓨터 프로그램에서 거의 모든 입출력이 문자열이니 미리 문자열로 만들어 한꺼번에 출력하는 것도 매우 좋은 방법입니다. 바로 이때 수치형 데이터들을 문자열로 바꾸는 방법입니다.

여기서 물론 itoa(), ftoa() 함수들의 존재가 궁금하겠지만 이 함수들은 표준이 아니어서 사용하는 컴파일러에 따라 제공되지 않을 수 있습니다.

그리고 인자로 문자열의 사이즈를 함께 넘겨줄 수 있는 snprintf() 와 snscanf() 함수들을 지원하기도 하지만 역시 표준 함수가 아닙니다. 마지막 인자로 나온 ...에 대한 것은 Advanced Pointer II 글타래에서 가변인자를 설명하면서 자세히 다루겠습니다. 모두 <stdio.h>에 프로토타입 선언이 되어 있습니다.



                                    

int atoi(const char * s);         // == (int)strtol(s,0,10) ;

long atol(const char * s);     // == strtol(s,0,10) ;

double atof(const char * s);     // == strtod(s,0) ;


문자열을 수치형 데이터로 만들어 주는 함수들입니다. 문자열에서 수 문자가 아닌 최초의 문자 이전까지의 수 문자열을 int형, long형, double형 데이터로 변화시켜 줍니다. 특히 atof() 함수는 float형이 아닌 double형을 리턴 한다는 것을 기억해야 합니다. <stdlib.h>에 선언되어 있습니다.

      

    printf("%d\n" , atoi("a12345")) ;

    // == 0

    printf("%d\n" , atoi("123a45")) ;

    // == 123

    printf("%d\n" , atoi("-123a45")) ;

    // == -123

    printf("%d\n" , atoi("+123a45")) ;

    // == 123

    printf("%d\n" , atoi("0000123a45")) ;

    // == 123

    printf("%d\n" , atoi("00001.23a45")) ;

    // == 1

    printf("%d\n" , atoi("999999999999999")) ;

    // == 2147483647, 범위를 넘어가면 int최대값을 리턴

    printf("%d\n" , atoi("-999999999999999")) ;

    // == -2147483648, 범위를 넘어가면 int최소값을 리턴   

    printf("%f\n" , atof("a12345")) ;

    // == 0.000000

    printf("%f\n" , atof("1.23a45")) ;

    // == 1.230000

    printf("%f\n" , atof("-1.2.3a45")) ;

    // == -1.200000

    printf("%f\n" , atof("+123.a45")) ;

    // == 123.000000

    printf("%f\n" , atof("000012.3a45")) ;

    // == 12.300000

    printf("%f\n" , atof("999999999999999")) ;

    // == 999999999999999.000000

    printf("%f\n" , atof("-999999999999999")) ;

    // == -999999999999999.000000         




내가 원하는 걸 콕 찝어 확인해주는 고맙고 멋진 녀석들


                              

int isalnum(int c);

int isalpha(int c);

int iscntrl(int c);

int isdigit(int c);

int isgraph(int c);

int islower(int c);

int isprint(int c);

int ispunct(int c);

int isspace(int c);

int isupper(int c);

int isxdigit(int c);

int tolower(int c);

int toupper(int c);


함수 이름 자체가 모든 것을 설명하고 있으므로 따로 언급하지는 않겠습니다. 아래 그림은 문자 검사 함수들의 일반적인 관계도 입니다. 재미있는 사실은 char를 다루는 함수들임에도 불구하고 인자와 리턴값들이 모두 int라는 것입니다. 이건 왜 그럴까요? 한번 고민해 보시길 바랍니다.



† 다른 여러 종류의 컴파일러에 따른 제어문자들

‡ 각 지역별 특수 알파벳문자들









C언어 및 기타 프로그래밍 관련 질문은 오픈 카톡으로

group talk - https://is..gd/yourc

1:1 talk - https://is.gd/aboutc

#Sinclair #씽클레어 #싱클레어 #씽클레어도씨 #씨언어 #씨프로그래밍  #C언어 #Cprogramming #C_Programming #C #Programming #Clanguage #C_Language

매거진의 이전글 Advanced Pointer I
작품 선택
키워드 선택 0 / 3 0
댓글여부
afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari