C언어 문자열 함수

Minimal_user·2024년 5월 12일

c언어

목록 보기
12/17

1. 문자열 함수

  • c언어에서 문자열은 널 종료 문자가 끝에 있는 문자 배열로 표현하며 기본 타입에 포함되지 않는다.
    • 정수나 실수와 같은 기본 타입으로 인정되지 않기 때문에 컴파일러가 제공하는 =, ==, +, - 등의 기본적인 연산자를 자유롭게 사용할 수 없다.
  • 문자열 함수들은 대부분 string.h에 선언되어 있으므로 이 헤더 파일을 include해야 한다.

1.1 문자 배열에 문자열을 복사하는 함수

  • char *strcpy(char *dest, const char *src);
    • 함수 원형에 const라는 지정자가 붙어 있는데 이는 함수가 인수의 내용을 바꾸지 않는다는 뜻이다.
    • src의 제일 끝에 있는 널 종료 문자도 같이 dest에 복사된다.
    • 리터값으로 dest의 번지가 다시 돌려지는데 특별한 의미는 없으며 잘 사용되지도 않는다.
    • strcpy함수는 src문자열 전체를 널 종료 문자를 만날 때까지 dest로 복사한다.
      • c언어 특성상 strcpy 함수도 인수로 주어진 문자 배열의 끝을 점검하지 않으며 이 배열의 크기가 얼마인지도 모른다.
        그래서 strcpy 함수로 문자열을 복사할 때는 항상 dest가 src의 문자열을 대입받을만한 충분한 크기를 가지도록 해야 한다.(물론 널 종료 문자도 배열 크기에 당연히 포함시켜야 한다.)
    • 배열의 길이가 충분하지 않을 경우 strcpy는 배열 뒤쪽의 메모리까지 덮어써버리면서 dest로 전달받은 변수에 인접해 있는 다른 변수가 파괴되면서 치명적인 에러가 발생한다.

  • char *strncpy(char *dest, const char *src, size_t count);
    • strcpy 함수와 비슷하지만 세 번째 인수 count가 추가되어 있다.
    • count는 복사할 문자 개수를 지정하는데 정확하게 이 개수만큼만 dest로 복사되며 널 종료 문자는 따로 붙이지 않는다.
    • 이 함수는 지정한 개수만큼만 문자를 복사하므로 널 종료 문자가 개수에 포함되지 않으며 같이 복사되지 않는다.
    • dest에 이미 문자들(문자열)이 저장되어 있을 때 복사할 문자의 길이를 src의 길이보다 짧게 지정하면
      dest에 지정한 길이만큼의 src 문자를 덮어씌운다(복사한다).
      즉, dest에는 지정한 길이 만큼 src의 문자가 덮어씌워지고 뒷 부분은 그대로 유지된다.
    • 반대로 src의 길이보다 길게 지정하면 src만큼 덮어씌우고 나머지 부분을 널 문자로 채운다.
    • strcpy 함수는 문자열 전체를 완전히 복사할 때 사용하며 strncpy 함수는 문자열의 일부분을 다른 부분 문자열로 바꾸고 싶을 때 사용한다.
    • 만약 문자열 시작 부분이 아니라 중간의 일부를 바꾸고 싶다면 복사할 위치를 옯기면 된다.
      strncpy(str2+2, str1+2, 3);

  • size_t strlen(const char *string);
    • 문자열의 시작 번지만 주면 이 번지에 들어있는 문자열의 길이를 조사한다.
    • 문자열이 저장된 배열의 크기가 실제로 저장된 문자의 갯수보다 크더라도 실제로 저장된 문자의 갯수만 반환함.
    • 한글의 경우 2문자로 계산되어 반환함.

1.2 문자열 연결

  • char *strcat(char *dest, const char *src);
    char *strncat(char *dest, const char *src, size_t count);
    • 두 함수는 한 문자열(dest) 끝에 다른 문자열(src)을 덧붙이는 것이다.
    • 정확히는 dest의 끝에 있던 널 종료 문자 자리에 src 문자열이 추가된다.
      src문자열 끝에 있는 널 종료 문자가 같이 추가되므로 연결 후 dest는 완전한 문자열이 된다.
    • strcpy 함수에서와 마찬가지로 strcat 함수도 배열의 끝 점검을 하지 않고 무조건 문자열을 연결하므로 dest는 합쳐질 문자열의 길이까지 고려하여 충분한 크기를 가지고 있어야 한다. 그렇지 않으면 dest 뒤쪽의 메모리가 파괴된다.
    • strncat함수는 문자열을 합치지되 합칠 문자열의 개수를 지정할 수 있다.
    • strncpy함수와 달리 일부 문자열만 연결하더라도 항상 널 종료 문자를 뒤에 붙인다는 점만 다르다.
    • 여러 개의 문자열을 합칠 때는 sprintf 함수가 더 편리하다.

1.3 문자열 비교

  • int strcmp(const char *s, const char *s2);
    int strncmp(const char *s, const char *s2, size_t count);
    • 두 문자열이 같은지 다른지, 다르다면 어떤 문자열이 더 큰지를 비교한다.
    • 이 함수들은 인수로 주어진 두 번자열을 비교한 후 그 결과를 다음과 같이 정수값 하나로 리턴한다.
      • s1과 s2가 같으면 0, s1>s2 이면 양수, s1<s2이면 음수
      • 리턴값이 0, 1, -1 과 같이 상수로 정의되어 있지 않고, 부호로 정의되어 있는 이유는 비교속도를 높이기 위해서이다.
      • strcmp 함수는 두 문자열의 대응되는 문자들을 차례대로 비교해 나가다가 최초로 다른 문자가 발견되면 두 문자의 코드값을 그대로 뺄셈해서 리턴해 버린다.
    • strncmp는 지정한 개수만큼만 문자열을 비교한다.

1.4 문자열 검색

  • 문자열 검색 함수는 문자열 중 특정 문자나 부분 문자열의 위치를 찾아 주는데 다음과 같은 것들이 있다.
    원형이 대체로 비슷한데 첫 번째 인수로 검색 대상 문자열을 주고, 두 번째 인수로 검색할 문자(열)을 주며 리턴값은 모두 문자형 포인터다.
  • char *strchr(const char *string, int c);
    char *strrchr(const char *string, int c);
    char *strstr(const char *string, const char *strSearch);
    char *strpbrk(const char *string, const char *strCharSet);
    char *strtok(char *strToken, const char *strDelimit);
  • strchr 함수는 문자열 중에 c라는 문자가 있는지를 찾아 그 포인터를 리턴한다.
char str[]="notrbook";
char *ptr=strchr(str, 'b');
*ptr='c'; // ptr이 가리키는 대상체를 'c'로 변경한다.
  • 만약 지정된 문자가 발견되지 않으면 strchr 함수는 NULL을 리턴하는데 이때 NULL은 찾는 대상이 없다는 뜻이다
    • strchr 함수뿐만 아니라 모든 문자열 검색함수들은 찾는 대상이 없으면 항상 NULL을 리턴하도록 되어 있다.
    • 문자열 중 특정 문자의 위치는 관심없고 단지 문자의 포함 여부만 알고 싶을 때는 리턴값이 NULL인지 아닌지만 비교하면 된다.
  • strrchr 함수는 문자열의 뒤에서부터 문자를 검색한다.
  • strchr을 이용하여 지정한 문자가 몇 번 포함되었는지 확인할 수 있는 방법은
    • strchr에 찾을 문자를 적용하고 리턴받은 포인터가 있으면 1카운팅하고 리턴 받은 포인터를 또 strchr에 적용하면 됨.
  • strstr 함수는 문자열에서 부분 문자열을 찾는다.
    • 부분 문자열을 구성하는 일련의 분자들이 연속적으로 발견될 때 그 시작 번지를 리턴한다.
    • strcmp 함수는 두 문자열이 정확히 같은지 비교하는데 비해, strstr 함수는 부분 문자열의 포함여부를 조사할 때 흔히 많이 사용된다.
  • strpbrk 함수는 문자열 검색 함수 중에 가장 복잡하여 사용 빈도도 낮지만 잘 알아 두면 여러 번 검색해야 하는 작업을 간단하게 끝낼 수 있다.
    • 이 함수는 첫 번째 인수로 주어진 문자열에서 두 번째 인수로 주어진 문자열에 속해 있는 문자 중 가장 먼저 발견된 문자를 찾아 그 번지를 리턴한다.
      • char str[]="Four score and seven years ago"; char *ptr=strpbrk(str, "def");
      • str 문자열 중에서 d나 e나 f 중 가장 먼저 발견되는 문자를 찾아 ptr에 대입한다.
    • 문자열에서 여러 개의 문자 중 하나를 찾고자할 때 이 함수를 사용하는데 strchr을 여러 번 호출할 필요없이 찾고자 하는 문자의 집합을 문자열로 만든 후 strpbrk함수만 호출하면 된다.
    • 만약 원하는 문자가 하나도 발견되지 않으면 NULL을 리턴한다.
  • strtok 함수는 문자열을 토큰으로 잘라낸다.
    • 첫 번째 인수로 잘라낼 문자열을 주며, 두 번째 인수로 구분자를 구성하는 문자열을 준다.
      • 구분자는 한 문자열에 여러 개를 지정할 수 있다.
    • strtok 함수는 최초 호출될 때 문자열의 첫 번째 토큰을 찾고 두 번째 토큰 위치를 NULL 문자로 만든 후 토큰의 포인터를 리턴한다. (이 포인터를 읽으면 변경된 NULL문자까지 읽는다.)
      • 검색한 토큰을 널 종료 문자열로 만들어 주므로 strtok가 리턴하는 포인터를 바로 출력하거나 별도의 버퍼에 복사하면 분리된 토큰을 얻을 수 있다.
      • 이때 strtok 함수는 중간 검색 결과를 자신의 정적변수에 저장해 놓는데 검색을 계속하려면 첫 번째 인수를 NULL로 전달하면 된다.
        • 더 이상 토큰이 발견되지 않으면 NULL을 리턴하므로 strtok(NULL, 구분자)를 반복적으로 호출하면 문자열을 구성하는 모든 토큰을 찾을 수 있다.
    • strtok 함수는 무척 독특한 특징이 몇 가지 있어 주의해서 사용해야 한다.
      • 우선 토큰이 발견될 때마다 널 문자열로 만들기 위해 검색 대상 문자열을 변경한다는 점이 다른 검색함수와 다르다.
        • 그래서 검색 대상 문자열이 변경되지 말아야할 경우는 반드시 사본을 복사한 후 사본에서 토큰을 추출해야 한다.
      • 또한 이 함수는 검색 중간 결과, 즉 어디까지 검색했는지를 스스로의 정적변수에 저장한다는 점이 특이하다.
        • 정적변수를 사용하기 때문에 검색 중에 새로운 검색을 할 경우 이전 검색에 대한 정보는 잃어버린다.
        • 과거 환경에서는 이런 특성이 편리했지만 요즘 같은 멀티 스레드 환경에서는 이런 동작 방식은 권장되지 않으며, 여러 가지 문제점을 야기할 수 있다.
          • 두 개의 스레드가 동시에 이 함수를 호출할 경우 결과는 예측할 수 없다.
char str[]="I am a boy,you are a girl";
char *p;

p=strtok(str," ,"); // ' ' 과 ','를 모조리 구분한다.
while (p!= NULL) {
	puts(p);
	p=strtok(NULL," ,");
}

1.5 문자열 변환 (일부 VS에서만 지원)

  • 문자열 변환이란 문자열의 내용을 일정한 규칙에 따라 바꾸는 것이다.
  • char *strset(char *string, int c);
    char *strnset(char *string, int c, size_t count);
    char *strlwr(char *string);
    char *strupr(char *string);
    char *strrev(char *string);
  • strset 함수는 문자열을 c문자로 가득 채운다. 널 문자 직전까지의 모든 문자들이 c문자로 바뀐다.
  • strnset 함수는 개수를 지정할 수 있다는 것만 다르다.
  • strlwr 함수는 모든 문자를 소문자로 바꾸며, strupr 함수는 모든 문자를 대분자로 바꾼다.
    • 두 함수 모두 영문자가 아닌 한글이나 숫자, 기호는 그대로 유지한다.
  • strrev 함수는 문자열을 거꾸로 뒤집는다.
    • 즉, 제일 처음에 있는 문자를 마지막 문자와 교환하고, 두 번째 문자는 마지막 두 번째 문자와 교환하는 식이다.
    • 널 종료 문자는 교환 대상에서 제외된다.
    • 이 함수는 문자열을 구성하는 개별 문자를 기계적으로 교환할 뿐이므로 한글에 대해서는 제대로 동작하지 않는다.

1.6 문자 관리 함수

  • 문자열을 구성하는 개별 문자들을 관리하는 함수들은 인수로 전달된 문자가 특정 그룹에 속하는지 조사하는데 그룹에 속하면 0이 아닌 값을 리턴하고 그렇지 않다면 0을 리턴한다.
  • ctype.h에 선언되어 있다.
함수참이 되는 문자 범위
int isalpha(int c);A-Z, a-z
int isupper(int c);A-Z
int islower(int c);a-z
int isdigit(int c);0-9
int isxdigit(int c);0-9, A-F, a-f
int isalnum(int c);0-9, A-Z, a-z
int isprint(int c);인쇄 가능한 문자
int isgraph(int c);공백을 제외한 인쇄 가능한 문자
int ispunct(int c);인쇄 가능한 문자 중 공백과 isalnum을 제외한 문자
int isspace(int c);0x09-0x0d, 0x20
  • 특정 문자가 알파벳인지, 숫자인지 등을 판단하고 싶을 때 이 함수들을 호출한다.

  • int tolower(int c);, int toupper(int c); 함수는 영문 대문자를 변환한다. (ctype.h 를 include 해야 한다)
    • 만약 c가 영문자가 아니라면 아무 동작도 하지 않으며 c를 그대로 리턴한다.
    • 활용 예로 if(ch=='A'|| ch=='a') 보다 if(tolower(ch)=='a')로 짧고 편하게 쓸 수 있다.

1.7 메모리 관리 함수

  • 문자열과 메모리는 연속된 공간이라는 점에서 공통점이 있기 때문에 메모리 관리함수들의 동작도 문자열 관리 함수들과 비슷하다. 동작이 비슷하다 보니 함수 이름과 원형도 거의 같다.
  • void *memcpy(void *dest, const void *src, size_t count);
    int *memcmp(const void *buf1, const void *buf2, size_t count);
    void *memchr(const void *buf, int c, size_t count);
    void *memset(void *dest, int c, size_t count);
    void *memmove (void *dest, const viod *src, size_t count);
  • strcpy에 대응되는 memcpy, strcmp에 대응되는 memcmpy 등의 함수들이 있는데 기본적인 동작 방식은 대응되는 문자열 함수와 같다고 생각하면 된다. 다만 몇 가지 점만 다른데 메모리 관리 함수들과 문자열 관리 함수들의 차이점은 다음과 같다.
    • 인수와 리턴값의 타입이 다르다. 문자열 관리 함수들은 항상 문자열을 대상으로 하므로 취하는 인수나 리턴값이 대부분 char *형이지만 메모리 조작함수들은 임의의 값을 대상으로 하기 때문에 인수와 리턴값이 모두 void *형이다. 이 함수들은 시작 번지만 알려주면 바이트 단위로 작업을 하므로 메모리에 저장된 값의 타입을 몰라도 상관없으며 임의의 타입에 대해서도 잘 동작한다.
    • 문자열은 시작 번지만 알려 주면 널 종료 문자를 끝으로 인식하기 때문에 길이를 별도로 알려줄 필요가 없다. 하지만 메모리 관리 함수는 길이를 알려주지 않으면 어디까지가 작업 대상인지를 알지 못한다. 그래서 모든 함수의 끝에 작업 대상 메모리의 길이를 지정하는 count라는 인수가 있다. 메모리끼리 복사하는 memcpy 함수는 strcpy 함수보다는 strncpy 함수와 유사하다고 볼 수 있다.
  • 이 두 가지 차이점만 알면 문자열 함수와 비슷한 방식으로 메모리 관리 함수를 사용할 수 있다.
  • 메모리 관리 함수 중에 가장 재미잇고도 실용적인 함수는 memmove함수다.
    • 이 함수는 메모리 내용을 지정한 길이만큼 다른 곳으로 옮기다. 이 함수를 사용하면 매열 중간을 뒤쪽을 밀어서 빈 공간을 만든 후 그 공간에 다른 내용을 삽입해 넣을 수 있다.
    • 이 함수를 사용할 때는 dest의 크기가 넉넉해야 한다.
    • 이름에 move가 포함되어 있기는 하지만 그렇다고 해서 원본이 삭제되는 것은 아니며 복사된다.
    • memcpy와 memmove는 메모리 내용을 복사한다는 점에서 사실 동일한 함수다. 그러나 memcpy는 무조건 순방향으로 복사하는데 비해 memmove는 겹쳐진 메모리에 대해서 원본이 파괴되지 않도록 복사 방향을 지능적으로 선택한다는 점이 다르다.

2. 수치와 문자열

2.1 정수와 문자열

  • char *itoa(int value, char *string, int radix);
    char *ltoa(long value, char *string, int radix);
    char *ultoa(unsigned long value, char *string, int radix);
  • 이 함수들은 정수를 문자열로 변환한다.
    • 정수 타입에 따라서 세 개의 함수가 준비되어 있는데 32비트 환경에서는 long형이 int와 같기 때문에 ltoa 함수는 itoa 함수와 동일하다.
    • 주로 itoa 함수를 많이 사용하며 부호없는 정수일 경우에만 ultoa 함수를 사용한다.
    • 참고 : itoa 함수 이름의 의미는 Integer to Ascii의 준말이다.
  • itoa 함수의 첫 번째 인수에 바꾸고자하는 정수값을 주고 두 번째 인수에 변환한 문자열을 저장할 배열을 준다. 세 번째 인수 radix는 변환시 사용할 진법을 지정하는데 10진법으로 변환할 경우 10이라고 쓰면 된다.
    • 이 함수도 배열 끝 점검을 하지 않으므로 두 번째 인수로 충분한 길이의 배열을 제공해야 한다.

  • int atoi(const char *string);
    long atol(const char *string);
    long strtol(const char *nptr, char **endptr, int base);
    unsigned long strtoul(const char *nptr, char **endptr, int base);
  • 위 함수들은 문자열을 정수로 바꾸는 함수다.
  • atoi 함수의 인수로 전달되는 문자열 수치값의 형태로 사용할 수 있는 문자는 0~9까지의 아라비아 숫자와 +, - 부호 정도이다.
    • 수치가 아닌 문자열을 전달할 경우 atoi는 변환할 수 없다는 의미로 0을 리턴한다.
    • 만약 수치와 문자열이 섞여 있다면 atoi는 에러를 리턴하는 대신 변환 가능한 부분까지만 변환한다.
  • atol 함수는 문자열에 저장된 수치를 long 형으로 변환하는데 32비트 환경에서는 int와 long이 같은 타입이므로 atoi와 완전히 같은 동작을 한다.
  • strtol 함수는 atoi 함수에 비해 진법을 인식한다. 세 번째 인수 base로 문자열에 들어 있는 값의 진법을 지정할 수 있다.
    • 지정한 base에 사용할 수 없는 문자가 발견될 경우 그 위치에서 변환을 중지한다.
      • 이때 변환이 중지된 지점의 번지가 두 번째 인수 endptr에 대입되므로 어디서 중지되었는지를 조사할 수 있다.
      • 만약 에러 검출 기능을 사용하지 않는다면 strtol의 두 번째 인수로 NULL을 지정하면 된다.

2.2 실수와 문자열

  • 실수는 정수에 비해 소수점 이하를 가질 뿐만 아니라 부동 소수점 표기법이 다소 복잡해서 정수보다는 변환하기가 훨씬 까다롭다.
  • char *gcvt(double value, int digits, char *buffer);
    char *ecvt(double value, int count, int *dec, int *sign);
    char *fcvt(double value, int count, int *dec, int *sign);
  • 이 함수들 보다 더 좋은 변환 방법이 있기 때문이 이 함수들이 실제로 꼭 필요한 경우란 드물다.

  • double atof(const char *string);
    double strtod(const char *nptr, char **endptr);
  • 위 함수들은 문자열을 실수로 변환한다.
  • 두 함수는 기능이 거의 동일하되 strtod 함수는 변환 불가 문자를 만날 때 그 위치를 endptr로 리턴한다는 정도만 다르다.
  • 편의성 면에서는 atof가 좀 더 사용하기 쉽다.
  • atof("-1.23e4") 형식의 부동 소수점 형태로 표기된 문자열도 변환할 수 있다.
  • atof 함수도 atoi 함수와 마찬가지로 중간에 변환 불가능한 문자를 만날 경우 에러를 리턴하는 대신 가능한 부분까지만 변환한다.

2.3 sprintf

  • int sprintf(char *buffer, const char *format, ...);
  • printf 함수의 원형과 비교해 볼 때 첫 번째 인수 buffer가 추가되어 있다는 점만 다르다.
  • 그 외 사용하는 방법이나 %d, %f 등의 서식은 printf 함수와 동일하다.
  • printf는 서식화된 출력을 화면으로 내 보내는데 비해, sprintf 함수는 첫 번째 인수로 전달된 buffer 배열로 보낸다는 점이 다르다.
  • 이 함수로 서식화된 문자열을 조립한 후 화면으로 그대로 출력할 수도 있고 파일로도 출력할 수 있으며 그래픽 화면으로도 출력할 수 있다.
  • sprintf 함수는 서식화된 출력을 문자열로 작성하는 것이 원래의 목적이지만 수치값을 문자열 형태로 변환할 때도 사용할 수 있다.
  • sprintf 함수 하나만 있어도 대부분의 수치를 문자열로 바꿀 수 있긴하지만, 그러나 이 함수에도 단점이 있는데 너무 덩치가 커서 실행 파일이 커진다는 것과 속도가 느리다는 점이다.

출처 : 혼자 연구하는 C/C++ 1 / 김상형 저 / 와우북스

profile
White book for everything I need.

0개의 댓글