C Language                
            문자열
        
        
            
    수색…
소개
 C에서 문자열은 내장 유형이 아닙니다. C- 문자열은 '\0' 의해 널 문자로 끝나는 1 차원 문자 배열을 갖는 규칙입니다. 
 즉, 내용이 "abc" C 문자열은 'a' , 'b' , 'c' 및 '\0' 4 개의 문자를 갖게됩니다. 
통사론
- char str1 [] = "안녕하세요, 세상!"; / * 수정 가능 * /
- char str2 [14] = "안녕하세요, 세상!"; / * 수정 가능 * /
- char * str3 = "Hello, world!"; / * 변경 불가능 * /
길이 계산 : strlen ()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) 
{
    /* Exit if no second argument is found. */
    if (argc != 2) 
    {
        puts("Argument missing.");
        return EXIT_FAILURE;
    }
    size_t len = strlen(argv[1]);
    printf("The length of the second argument is %zu.\n", len);
    return EXIT_SUCCESS;
}
 이 프로그램은 두 번째 입력 인수의 길이를 계산하고 그 결과를 len 저장합니다. 그런 다음 해당 길이를 터미널에 인쇄합니다. 예를 들어 program_name "Hello, world!" 매개 변수 program_name "Hello, world!" , 프로그램이 출력 The length of the second argument is 13. Hello, world! 문자열이기 때문 Hello, world! 13 자입니다. 
 strlen 은 문자열의 시작부터 종료 NUL 문자 '\0' 까지의 모든 바이트 를 계산합니다. 따라서이 문자열은 문자열이 NUL로 끝나는 것이 보장 될 때만 사용할 수 있습니다. 
 또한 문자열에 유니 코드 문자가 포함되어 있으면 strlen 은 문자열에 몇 개의 문자가 포함되어 있는지 알려주지 않습니다 (일부 문자는 여러 바이트가 될 수 있기 때문에). 이 경우 문자 ( 즉 코드 단위)를 직접 계산해야합니다. 다음 예제의 출력을 고려하십시오. 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) 
{
    char asciiString[50] = "Hello world!";
    char utf8String[50] = "Γειά σου Κόσμε!"; /* "Hello World!" in Greek */
    printf("asciiString has %zu bytes in the array\n", sizeof(asciiString));
    printf("utf8String has %zu bytes in the array\n", sizeof(utf8String));
    printf("\"%s\" is %zu bytes\n", asciiString, strlen(asciiString));
    printf("\"%s\" is %zu bytes\n", utf8String, strlen(utf8String));
}
산출:
asciiString has 50 bytes in the array
utf8String has 50 bytes in the array
"Hello world!" is 12 bytes
"Γειά σου Κόσμε!" is 27 bytes
복사 및 연결 : strcpy (), strcat ()
#include <stdio.h>
#include <string.h>
int main(void)
{
  /* Always ensure that your string is large enough to contain the characters
   * and a terminating NUL character ('\0')!
   */
  char mystring[10];
  /* Copy "foo" into `mystring`, until a NUL character is encountered. */
  strcpy(mystring, "foo");
  printf("%s\n", mystring);
  /* At this point, we used 4 chars of `mystring`, the 3 characters of "foo",
   * and the NUL terminating byte.
   */
  /* Append "bar" to `mystring`. */
  strcat(mystring, "bar");
  printf("%s\n", mystring);
  /* We now use 7 characters of `mystring`: "foo" requires 3, "bar" requires 3
   * and there is a terminating NUL character ('\0') at the end.
   */
  /* Copy "bar" into `mystring`, overwriting the former contents. */
  strcpy(mystring, "bar");
  printf("%s\n", mystring);
  return 0;
}
출력 :
foo
foobar
bar
기존 문자열에 추가하거나 기존 문자열에서 복사하는 경우 NUL 종료인지 확인하십시오!
 문자열 리터럴 (예 : "foo" )은 항상 컴파일러에서 NUL 종료됩니다. 
비교 : strcmp (), strncmp (), strcasecmp (), strncasecmp ()
 strcase* - 함수는 표준 C가 아니라 POSIX 확장입니다. 
 strcmp 함수는 사전 식으로 두 개의 null로 끝나는 문자 배열을 비교합니다. 함수는, 최초의 인수가 2 번째의 사전 순서로 출현하고있는 경우는 부의 값, 동등의 경우는 0, 3 번째의 인수는 사전 순서로 2 번째의 인수가 출현하는 경우는 부의 값을 돌려줍니다. 
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs)
{
    int result = strcmp(lhs, rhs); // compute comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}
int main(void)
{
    compare("BBB", "BBB");
    compare("BBB", "CCCCC");
    compare("BBB", "AAAAAA");
    return 0;
}
출력 :
BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA
 strcmp 와 마찬가지로 strcasecmp 함수는 각 문자를 소문자로 변환 한 후 사전 식으로 인수를 비교합니다. 
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs)
{
    int result = strcasecmp(lhs, rhs); // compute case-insensitive comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}
int main(void)
{
    compare("BBB", "bBB");
    compare("BBB", "ccCCC");
    compare("BBB", "aaaaaa");
    return 0;
}
출력 :
BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa
 strncmp 와 strncasecmp 는 최대 n 문자를 비교합니다. 
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs, int n)
{
    int result = strncmp(lhs, rhs, n); // compute comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}
int main(void)
{
    compare("BBB", "Bb", 1);
    compare("BBB", "Bb", 2);
    compare("BBB", "Bb", 3);
    return 0;
}
출력 :
BBB equals Bb
BBB comes before Bb
BBB comes before Bb
토큰 화 : strtok (), strtok_r () 및 strtok_s ()
 strtok 함수는 문자열을 작은 문자열 또는 토큰으로 구분 기호 세트를 사용하여 구분합니다. 
#include <stdio.h>
#include <string.h>
int main(void)
{
    int toknum = 0;
    char src[] = "Hello,, world!";
    const char delimiters[] = ", !";
    char *token = strtok(src, delimiters);
    while (token != NULL)
    {
        printf("%d: [%s]\n", ++toknum, token);
        token = strtok(NULL, delimiters);
    }
    /* source is now "Hello\0, world\0\0" */
}
산출:
1: [Hello]
2: [world]
 구분 기호 문자열에는 하나 이상의 구분 기호가 포함될 수 있으며 strtok 호출 할 때마다 다른 구분 기호 문자열을 사용할 수 있습니다. 
 동일한 소스 문자열을 토큰 화하는 작업을 계속하기 위해 strtok 을 호출하면 소스 문자열을 다시 전달해서는 안되며 대신 첫 번째 인수로 NULL 을 전달 NULL . 동일한 소스 문자열 이 전달되면 첫 번째 토큰이 다시 토큰 화됩니다. 즉, 동일한 분리 문자를 사용하면 strtok 은 첫 번째 토큰을 다시 반환합니다. 
 strtok 은 토큰에 새로운 메모리를 할당하지 않으므로 소스 문자열을 수정합니다 . 즉, 위의 예에서 문자열 src 는 strtok 에 대한 호출에서 반환 된 포인터가 참조하는 토큰을 생성하기 위해 조작됩니다. 즉, 소스 문자열을 const 지정할 수 없으므로 (문자열 리터럴이 될 수 없습니다). 또한 구분 바이트의 ID가 손실된다는 것을 의미합니다 (예 : ","및 "!"는 소스 문자열에서 효과적으로 삭제되고 어떤 구분 문자가 일치하는지 알 수 없음). 
소스 문자열의 여러 연속 구분 기호는 하나로 처리됩니다. 예제에서 두 번째 쉼표는 무시됩니다.
 strtok 은 파싱하는 동안 정적 버퍼를 사용하기 때문에 thread로부터 안전하지도 않고 re-entrant가 아닙니다. 이 함수가 호출하는 경우 있음을 의미 strtok , 그것을 사용하는 동안은 전화 기능 없음 strtok 사용할 수도 있습니다 strtok , 그리고 그것을 사용하여 자신을 어떤 함수가 호출 할 수 없습니다 strtok . 
 strtok 이 재진입 할 수 없다는 사실로 인해 발생하는 문제를 보여주는 예는 다음과 같습니다. 
char src[] = "1.2,3.5,4.2";
char *first = strtok(src, ","); 
do 
{
    char *part;
    /* Nested calls to strtok do not work as desired */
    printf("[%s]\n", first);
    part = strtok(first, ".");
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok(NULL, ".");
    }
} while ((first = strtok(NULL, ",")) != NULL);
산출:
[1.2]
 [1]
 [2]
 예상되는 작업은 outer do while 루프가 각 10 진수 문자열 ( "1.2" , "3.5" , "4.2" )로 구성된 세 개의 토큰을 생성해야한다는 것입니다. 각 토큰은 내부 루프에 대한 strtok 호출이 별도의 ( "1" , "2" , "3" , "5" , "4" , "2" ). 
 그러나 strtok 은 재진입이 아니기 때문에 발생하지 않습니다. 대신 첫 번째 strtok 이 "1.2 \ 0"토큰을 올바르게 만들고 내부 루프가 토큰 "1" 과 "2" 올바르게 만듭니다. 그러나 외부 루프의 strtok 은 내부 루프가 사용하는 문자열의 끝에 있으며 즉시 NULL을 반환합니다. src 배열의 두 번째 및 세 번째 하위 문자열은 전혀 분석되지 않습니다. 
 표준 C 라이브러리는 thread-safe 또는 re-entrant 버전을 포함하지 않지만 POSIX의 strtok_r 과 같은 일부 버전은 포함합니다. MSVC에서 strtok 과 동등한 strtok_s 는 스레드로부터 안전합니다. 
 C11에는 strtok_s 라는 스레드 안전성과 재진입 성 버전을 제공하는 선택적 부분 인 Annex K가 있습니다. __STDC_LIB_EXT1__ 을 사용하여 기능을 테스트 할 수 있습니다. 이 선택적 부분은 널리 지원되지 않습니다. 
 strtok_s 함수는 POSIX strtok_r 함수와 달리 토큰 화 된 문자열 외부에 저장하는 것을 방지하고 런타임 제약 조건을 검사합니다. 하지만 제대로 작성된 프로그램에서는 strtok_s 와 strtok_r 이 똑같이 작동합니다. 
 예제와 함께 strtok_s 를 사용하면 strtok_s 과 같이 정확한 응답을 얻을 수 있습니다. 
/* you have to announce that you want to use Annex K */ 
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#ifndef __STDC_LIB_EXT1__
# error "we need strtok_s from Annex K"
#endif
char src[] = "1.2,3.5,4.2";  
char *next = NULL;
char *first = strtok_s(src, ",", &next);
do 
{
    char *part;
    char *posn;
    printf("[%s]\n", first);
    part = strtok_s(first, ".", &posn);
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok_s(NULL, ".", &posn);
    }
} 
while ((first = strtok_s(NULL, ",", &next)) != NULL);
출력은 다음과 같습니다.
[1.2]
 [1]
 [2]
[3.5]
 [3]
 [5]
[4.2]
 [4]
 [2]
특정 문자의 처음 / 마지막 항목 찾기 : strchr (), strrchr ()
 strchr 및 strrchr 함수는 문자열에서 NUL 종료 문자 배열에있는 문자를 찾습니다. strchr 은 첫 번째 항목에 대한 포인터를 반환하고 strrchr 은 마지막 항목에 대한 포인터를 반환합니다. 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    char toSearchFor = 'A';
    /* Exit if no second argument is found. */
    if (argc != 2)
    {
        printf("Argument missing.\n");
        return EXIT_FAILURE;
    }
    {
        char *firstOcc = strchr(argv[1], toSearchFor);
        if (firstOcc != NULL) 
        {
            printf("First position of %c in %s is %td.\n", 
              toSearchFor, argv[1], firstOcc-argv[1]); /* A pointer difference's result 
                                     is a signed integer and uses the length modifier 't'. */
        }
        else
        {
            printf("%c is not in %s.\n", toSearchFor, argv[1]);
        }
    }
    {
        char *lastOcc = strrchr(argv[1], toSearchFor);
        if (lastOcc != NULL)
        {
            printf("Last position of %c in %s is %td.\n",
              toSearchFor, argv[1], lastOcc-argv[1]);
        }
    }
    return EXIT_SUCCESS;
}
 출력 ( pos 라는 실행 파일을 생성 한 후) : 
$ ./pos AAAAAAA
First position of A in AAAAAAA is 0.
Last position of A in AAAAAAA is 6.
$ ./pos BAbbbbbAccccAAAAzzz
First position of A in BAbbbbbAccccAAAAzzz is 1.
Last position of A in BAbbbbbAccccAAAAzzz is 15.
$  ./pos qwerty             
A is not in qwerty.
 strrchr 한 가지 일반적인 용도는 경로에서 파일 이름을 추출하는 것입니다. 예를 들어 C:\Users\eak\myfile.txt 에서 myfile.txt 를 추출하려면 다음과 같이하십시오. 
char *getFileName(const char *path)
{
    char *pend;
    if ((pend = strrchr(path, '\')) != NULL)
        return pend + 1;
    return NULL;
}
문자열에서 문자 반복하기
문자열의 길이를 알면 for 루프를 사용하여 문자를 반복 할 수 있습니다.
char * string = "hello world"; /* This 11 chars long, excluding the 0-terminator. */
size_t i = 0;
for (; i < 11; i++) {
    printf("%c\n", string[i]);    /* Print each character of the string. */
}
 또는 문자열이 무엇인지 모르는 경우 표준 함수 인 strlen() 을 사용하여 문자열의 길이를 구할 수 있습니다. 
size_t length = strlen(string);
size_t i = 0; 
for (; i < length; i++) {
    printf("%c\n", string[i]);    /* Print each character of the string. */
}
 마지막으로, C의 문자열이 null로 종료된다는 사실을 활용할 수 있습니다 (앞의 예에서 strlen() 에 전달할 때 이미 strlen() .-)). 크기에 관계없이 배열을 반복 할 수 있고 널 문자에 도달하면 반복을 중지 할 수 있습니다. 
size_t i = 0;
while (string[i] != '\0') {       /* Stop looping when we reach the null-character. */
    printf("%c\n", string[i]);    /* Print each character of the string. */
    i++;
}
문자열에 대한 기본 소개
C에서 문자열 은 널 문자 ( '\ 0')로 끝나는 일련의 문자입니다.
 문자열 리터럴을 사용하여 문자열을 만들 수 있습니다. 문자열 리터럴 은 큰 따옴표로 묶인 문자 시퀀스입니다. 예를 들어, "hello world" 문자열 리터럴을 가져옵니다. 문자열 리터럴은 자동으로 null로 종료됩니다. 
 여러 가지 방법을 사용하여 문자열을 만들 수 있습니다. 예를 들어, char * 선언하고 문자열의 첫 번째 문자를 가리 키도록 초기화 할 수 있습니다. 
char * string = "hello world";
 char * 를 위와 같이 문자열 상수로 초기화 할 때 문자열 자체는 일반적으로 읽기 전용 데이터로 할당됩니다. string 은 배열의 첫 번째 요소에 대한 포인터이며 'h' 문자입니다. 
 문자열 리터럴은 읽기 전용 메모리에 할당되므로 수정할 수 없습니다 1 . 그것을 수정하려고 시도하면 정의되지 않은 동작 이 발생하므로 const 를 추가하여 다음과 같은 컴파일 타임 오류를 얻는 것이 좋습니다 
char const * string = "hello world";
유사한 효과 2를가 집니다.
char const string_arr[] = "hello world";
수정 가능한 문자열을 만들려면 문자 배열을 선언하고 다음과 같이 문자열 리터럴을 사용하여 내용을 초기화 할 수 있습니다.
char modifiable_string[] = "hello world";
이것은 다음과 같습니다.
char modifiable_string[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
 두 번째 버전에서는 중괄호로 묶인 이니셜 라이저를 사용하므로 문자 배열에 '\0' 문자가 명시 적으로 포함되어 있지 않으면 문자열이 자동으로 null로 끝나지 않습니다. 
 1 수정할 수없는 것은 문자열 리터럴의 문자를 수정할 수 없다는 것을 의미하지만 포인터 string 을 수정할 수 있음을 기억하십시오 (다른 곳을 가리킬 수 있거나 증가 또는 감소 할 수 있음). 
 2 두 문자열 모두 비슷한 의미를 가지므로 두 문자열의 문자를 수정할 수 없습니다. string 은 char 대한 포인터이며 수정 가능한 l 값 이므로 배열 string_arr 이 수정 불가능한 l 값인 동안 증가하거나 다른 위치를 가리킬 수 있으므로 수정할 수는 없습니다. 
문자열 배열 만들기
문자열의 배열은 몇 가지를 의미 할 수 있습니다.
-  요소가 char *s 인 배열
-  요소가 char의 배열 인 배열
다음과 같이 문자 포인터 배열을 만들 수 있습니다.
char * string_array[] = {
    "foo",
    "bar",
    "baz"
};
 기억하십시오 : 문자열 리터럴을 char * 에 할당하면 문자열 자체가 읽기 전용 메모리에 할당됩니다. 그러나 배열 string_array 는 읽기 / 쓰기 메모리에 할당됩니다. 즉, 배열의 포인터를 수정할 수는 있지만 포인터가 가리키는 문자열은 수정할 수 없습니다. 
 C에서 main argv (프로그램이 실행될 때 전달 된 명령 행 인수의 배열)에 대한 매개 변수는 char * : char * argv[] 의 배열입니다. 
문자 배열 배열을 만들 수도 있습니다. 문자열은 문자 배열이므로 문자열 배열은 요소가 문자 배열 인 배열입니다.
char modifiable_string_array_literals[][4] = {
    "foo",
    "bar",
    "baz"
};
이것은 다음과 같습니다.
char modifiable_string_array[][4] = {
    {'f', 'o', 'o', '\0'},
    {'b', 'a', 'r', '\0'},
    {'b', 'a', 'z', '\0'}
};
 배열의 두 번째 차원 크기로 4 를 지정합니다. null 배열 캐릭터를 포함 할 필요가 있기 때문에 우리 배열의 각 문자열은 실제로 4 바이트입니다. 
strstr
/* finds the next instance of needle in haystack 
   zbpos: the zero-based position to begin searching from
   haystack: the string to search in
   needle: the string that must be found
   returns the next match of `needle` in `haystack`, or -1 if not found
*/
int findnext(int zbpos, const char *haystack, const char *needle)
{
    char *p; 
    if (((p = strstr(haystack + zbpos, needle)) != NULL)
        return p - haystack;
    return -1;
}
 strstr 은 haystack (첫 번째) 인수에서 needle 가리키는 문자열을 검색합니다. 발견되면 strstr 은 발생 주소를 반환합니다. needle 찾을 수 없으면 NULL을 반환합니다. 우리는 zbpos 사용하여 같은 바늘을 반복해서 찾아 내지 못합니다. 첫 번째 인스턴스를 건너 뛰기 위해 zbpos 의 오프셋을 추가합니다. 다음 찾기 대화 상자를 구현하기 위해 메모장 클론이 findnext 이와 같이 호출 할 수 있습니다. 
/*
    Called when the user clicks "Find Next"
    doc: The text of the document to search
    findwhat: The string to find
*/
void onfindnext(const char *doc, const char *findwhat)
{
    static int i;
    if ((i = findnext(i, doc, findwhat)) != -1)
        /* select the text starting from i and ending at i + strlen(findwhat) */
    else
        /* display a message box saying "end of search" */
}
문자열 리터럴
 문자열 리터럴은 null로 끝나는 정적 지속 기간 의 char 배열을 나타냅니다. 저장 기간이 정적이므로 문자열 리터럴 또는 동일한 기본 배열에 대한 포인터는 자동 배열에 대한 포인터로는 사용할 수없는 여러 가지 방법으로 안전하게 사용할 수 있습니다. 예를 들어 함수에서 문자열 리터럴을 반환하면 잘 정의 된 동작이 나타납니다. 
const char *get_hello() {
    return "Hello, World!";  /* safe */
}
 역사적인 이유로 인해 문자열 리터럴에 해당하는 배열의 요소는 공식적으로 const 가 아닙니다. 그럼에도 불구하고 수정하려는 시도는 정의되지 않은 동작 입니다. 일반적으로 문자열 리터럴에 해당하는 배열을 수정하려고하는 프로그램이 충돌하거나 그렇지 않으면 오작동합니다. 
char *foo = "hello";
foo[0] = 'y';  /* Undefined behavior - BAD! */
 포인터가 문자열 리터럴을 가리킬 때 - 또는 때로는 할 수있는 곳 - 실수로 그러한 정의되지 않은 동작을 피하기 위해 포인터의 지시 대상 const 를 선언하는 것이 좋습니다. 
const char *foo = "hello";
/* GOOD: can't modify the string pointed to by foo */
반면에 문자열 리터럴의 기본 배열을 가리키는 포인터는 본질적으로 특수하지 않습니다. 그 값은 다른 것을 가리 키도록 자유롭게 수정할 수 있습니다 :
char *foo = "hello";
foo = "World!"; /* OK - we're just changing what foo points to */
 또한 char 배열의 초기화 프로그램은 문자열 리터럴과 동일한 형식을 가질 수 있지만 이러한 초기화 프로그램을 사용하면 초기화 된 배열에 문자열 리터럴의 특성을 부여하지 않습니다. 이니셜 라이저는 단순히 배열의 길이와 초기 내용을 지정합니다. 특히 명시 적으로 선언하지 않으면 요소를 수정할 수 있습니다. const : 
char foo[] = "hello";
foo[0] = 'y';  /* OK! */
문자열을 0으로 만드는 것
 memset 을 호출하여 문자열 (또는 다른 메모리 블록)을 0으로 만들 수 있습니다. 
 여기서 str 은 0이되는 문자열이고 n 은 문자열의 바이트 수입니다. 
#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>
int main(void)
{
  char str[42] = "fortytwo";
  size_t n = sizeof str; /* Take the size not the length. */
  printf("'%s'\n", str);
  memset(str, '\0', n);
  printf("'%s'\n", str);
  return EXIT_SUCCESS;
}
인쇄물:
'fortytwo'
''
다른 예시:
#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>
#define FORTY_STR "forty"
#define TWO_STR "two"
int main(void)
{
  char str[42] = FORTY_STR TWO_STR;
  size_t n = sizeof str; /* Take the size not the length. */
  char * point_to_two = strstr(str, TWO_STR);
  printf("'%s'\n", str);
  memset(point_to_two, '\0', n);
  printf("'%s'\n", str);
  memset(str, '\0', n);
  printf("'%s'\n", str);
  return EXIT_SUCCESS;
}
인쇄물:
'fortytwo'
'forty'
''
strspn 및 strcspn
 문자열이 주어진 경우 strspn 은 특정 문자 목록만으로 구성된 초기 하위 문자열 (길이)의 길이를 계산합니다. strcspn 은 나열된 문자열을 제외한 모든 문자로 구성된 초기 하위 문자열의 길이를 계산한다는 점을 제외 strcspn 비슷합니다. 
/*
  Provided a string of "tokens" delimited by "separators", print the tokens along
  with the token separators that get skipped.
*/
#include <stdio.h>
#include <string.h>
int main(void)
{
    const char sepchars[] = ",.;!?";
    char foo[] = ";ball call,.fall gall hall!?.,";
    char *s;
    int n;
    for (s = foo; *s != 0; /*empty*/) {
        /* Get the number of token separator characters. */
        n = (int)strspn(s, sepchars);
        if (n > 0)
            printf("skipping separators: << %.*s >> (length=%d)\n", n, s, n);
        /* Actually skip the separators now. */
        s += n;
        /* Get the number of token (non-separator) characters. */
        n = (int)strcspn(s, sepchars);
        if (n > 0)
            printf("token found: << %.*s >> (length=%d)\n", n, s, n);
        /* Skip the token now. */
        s += n;
    }
    printf("== token list exhausted ==\n");
    return 0;
}
 넓은 문자열을 사용하는 유사한 함수는 wcsspn 과 wcscspn . 그들은 같은 방식으로 사용됩니다. 
문자열 복사
포인터 할당은 문자열을 복사하지 않습니다.
 = 연산자를 사용하여 정수를 복사 할 수 있지만 = 연산자를 사용하여 C에서 문자열을 복사 할 수는 없습니다. C의 문자열은 null 문자로 끝나는 문자 배열로 표시되므로 = 연산자를 사용하면 주소 만 저장됩니다 ( 포인터). 
#include <stdio.h>
int main(void) {
    int a = 10, b;
    char c[] = "abc", *d;
    b = a; /* Integer is copied */
    a = 20; /* Modifying a leaves b unchanged - b is a 'deep copy' of a */
    printf("%d %d\n", a, b); /* "20 10" will be printed */
    d = c; 
    /* Only copies the address of the string - 
    there is still only one string stored in memory */
    
    c[1] = 'x';
    /* Modifies the original string - d[1] = 'x' will do exactly the same thing */
    printf("%s %s\n", c, d); /* "axc axc" will be printed */
    return 0;
}
 우리가 사용하기 때문에 위의 예는 컴파일 char *d 보다 char d[3] . 후자를 사용하면 컴파일러 오류가 발생합니다. C에서 배열에 할당 할 수 없습니다. 
#include <stdio.h>
int main(void) {
    char a[] = "abc";
    char b[8];
    b = a; /* compile error */
    printf("%s\n", b);
    return 0;
}
표준 함수를 사용하여 문자열 복사하기
 strcpy() 
  문자열을 실제로 복사하기 위해서, strcpy() 함수는 string.h 에서 사용 가능하다. 복사하기 전에 목적지에 충분한 공간을 할당해야합니다. 
#include <stdio.h>
#include <string.h>
int main(void) {
    char a[] = "abc";
    char b[8];
    strcpy(b, a); /* think "b special equals a" */
    printf("%s\n", b); /* "abc" will be printed */
    return 0;
}
 snprintf() 
  버퍼 오버런을 피하기 위해 snprintf() 사용할 수 있습니다. 템플릿 문자열을 구문 분석해야하기 때문에 성능상 가장 좋은 솔루션은 아니지만 표준 라이브러리에서 즉시 사용할 수있는 문자열을 복사하기위한 유일한 버퍼 제한 안전 함수이며 추가 단계없이 사용할 수 있습니다. 
#include <stdio.h>
#include <string.h>
int main(void) {
    char a[] = "012345678901234567890";
    char b[8];
#if 0
    strcpy(b, a); /* causes buffer overrun (undefined behavior), so do not execute this here! */
#endif
    snprintf(b, sizeof(b), "%s", a); /* does not cause buffer overrun */
    printf("%s\n", b); /* "0123456" will be printed */
    return 0;
}
 strncat() 
  strncat() ( strcat() 의 버퍼 오버플로 검사 버전 strncat() 을 사용하는 것이 더 좋은 성능을 가진 두 번째 옵션입니다. 복사 할 최대 바이트 수를 알려주는 세 번째 인수가 필요합니다. 
char dest[32];
dest[0] = '\0';
strncat(dest, source, sizeof(dest) - 1);
    /* copies up to the first (sizeof(dest) - 1) elements of source into dest,
    then puts a \0 on the end of dest */
 이 공식은 sizeof(dest) - 1 ; 이것은 strncat() 항상 null 바이트 (양호)를 추가하기 때문에 중요하지만 문자열의 크기 (혼동 및 버퍼 덮어 쓰기의 원인)에는 포함되지 않습니다. 
또한 빈 문자열이 아닌 문자열을 연결하는 대안은 훨씬 더 복잡합니다. 중히 여기다:
char dst[24] = "Clownfish: ";
char src[] = "Marvin and Nemo";
size_t len = strlen(dst);
strncat(dst, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
출력은 다음과 같습니다.
23: [Clownfish: Marvin and N]
 길이로 지정된 크기 대상 배열의 크기이지만 단말 널 바이트를 카운트하지 안에 남은 공간의 양이 아니라고하지만, 참고. 이로 인해 큰 덮어 쓰기 문제가 발생할 수 있습니다. 또한 약간 낭비입니다. 길이 인수를 올바르게 지정하려면 대상에있는 데이터의 길이를 알고 있으므로 strncat() 않도록 저장하면서 기존 내용의 끝에 null 바이트의 주소를 지정할 수 있습니다. 
    strcpy(dst, "Clownfish: ");
    assert(len < sizeof(dst) - 1);
    strncat(dst + len, src, sizeof(dst) - len - 1);
    printf("%zu: [%s]\n", strlen(dst), dst);
 이것은 이전과 같은 출력을 생성하지만 strncat() 은 복사를 시작하기 전에 dst 의 기존 내용을 스 캔할 필요가 없습니다. 
 strncpy() 
  마지막 옵션은 strncpy() 함수입니다. 당신이 먼저 와야한다고 생각할 지 모르지만, 그것은 두 가지 주요 문제가있는 오히려기만적인 기능입니다 : 
-  strncpy()를 통한 복사가 버퍼 한계에 도달하면 종료 null 문자는 기록되지 않습니다.
-  strncpy()항상 대상을 완전히 채 웁니다. 필요한 경우 null 바이트로 채 웁니다.
(그러한 기발한 구현은 역사적이며 처음에는 UNIX 파일 이름을 처리하기위한 것이 었습니다 )
이를 사용하는 유일한 올바른 방법은 수동으로 널 종료를 보장하는 것입니다.
strncpy(b, a, sizeof(b)); /* the third parameter is destination buffer size */
b[sizeof(b)/sizeof(*b) - 1] = '\0'; /* terminate the string */
printf("%s\n", b); /* "0123456" will be printed */
 그렇더라도 커다란 버퍼를 가지고 있다면, 널 패딩을 추가하기 때문에 strncpy() 를 사용하는 것이 매우 비효율적이게됩니다. 
문자열을 숫자로 변환 : atoi (), atof () (위험한, 사용하지 마십시오)
 경고 : atoi , atol , atoll 및 atof 함수는 본질적으로 안전하지 않습니다. 결과의 값을 나타낼 수없는 경우 동작은 정의되지 않습니다. (7.20.1p1) 
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
    int val;
    if (argc < 2)
    {
        printf("Usage: %s <integer>\n", argv[0]);
        return 0;
    }
    val = atoi(argv[1]);
    printf("String value = %s, Int value = %d\n", argv[1], val);
    return 0;
}
변환 할 문자열이 범위 내에있는 유효한 10 진 정수이면 함수가 작동합니다.
$ ./atoi 100
String value = 100, Int value = 100
$ ./atoi 200
String value = 200, Int value = 200
숫자로 시작하고 뒤에 다른 문자가 오는 문자열의 경우 초기 숫자 만 구문 분석됩니다.
$ ./atoi 0x200
0
$ ./atoi 0123x300
123
다른 모든 경우에는 동작이 정의되지 않습니다.
$ ./atoi hello
Formatting the hard disk...
 위의 모호함과 정의되지 않은 동작으로 인해 atoi 계열의 함수를 사용해서는 안됩니다. 
-  long int로 변환하려면atol()대신strtol()사용하십시오.
-  double로 변환하려면atof()대신strtod()사용하십시오.
-  long long int로 변환하려면atoll()대신strtoll()사용하십시오.
문자열 형식의 데이터 읽기 / 쓰기
형식이 지정된 데이터를 문자열에 씁니다.
int sprintf ( char * str, const char * format, ... );
 sprintf 함수를 사용하여 float 데이터를 문자열에 씁니다. 
#include <stdio.h>
int main ()
{
  char buffer [50];
  double PI = 3.1415926;
  sprintf (buffer, "PI = %.7f", PI);
  printf ("%s\n",buffer);
  return 0;
}
문자열에서 형식이 지정된 데이터를 읽습니다.
int sscanf ( const char * s, const char * format, ...);
 sscanf 함수를 사용하여 형식이 지정된 데이터를 구문 분석하십시오. 
#include <stdio.h>
int main ()
{
  char sentence []="date : 06-06-2012";
  char str [50];
  int year;
  int month;
  int day;
  sscanf (sentence,"%s : %2d-%2d-%4d", str, &day, &month, &year);
  printf ("%s -> %02d-%02d-%4d\n",str, day, month, year);
  return 0;
}
안전하게 문자열을 Number로 변환 : strtoX 함수
 C99 이후 C 라이브러리에는 문자열을 숫자로 해석하는 안전 변환 함수 집합이 있습니다. 이들의 이름은 strtoX 형식 strtoX , 여기서 X 는 변환의 대상 유형을 결정하기 위해 l , ul , d 등 중 하나입니다. 
double strtod(char const* p, char** endptr);
long double strtold(char const* p, char** endptr);
그들은 변환이 과다 또는 언더 플로우가 있는지 확인합니다.
double ret = strtod(argv[1], 0); /* attempt conversion */
/* check the conversion result. */
if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE) 
    return;  /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE) 
    return; /* numeric underflow in in string */
/* At this point we know that everything went fine so ret may be used */
 실제로 문자열에 숫자가 전혀없는 경우 strtod 의이 사용법은 0.0 반환합니다. 
 만족스럽지 않으면 추가 매개 변수 endptr 사용할 수 있습니다. 문자열에서 발견 된 숫자의 끝을 가리키는 포인터에 대한 포인터입니다. 위와 같이 0 또는 NULL 로 설정하면 무시됩니다. 
 이 endptr 매개 변수는 성공적인 변환이 있었는지 여부를 나타내며, 그렇다면 숫자가 끝난 위치를 나타냅니다. 
char *check = 0;
double ret = strtod(argv[1], &check); /* attempt conversion */
/* check the conversion result. */
if (argv[1] == check) 
    return; /* No number was detected in string */
else if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE) 
    return; /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE) 
    return; /* numeric underflow in in string */
/* At this point we know that everything went fine so ret may be used */
더 넓은 정수 타입으로 변환하는 유사한 함수가 있습니다 :
long strtol(char const* p, char** endptr, int nbase);
long long strtoll(char const* p, char** endptr, int nbase);
unsigned long strtoul(char const* p, char** endptr, int nbase);
unsigned long long strtoull(char const* p, char** endptr, int nbase);
 이 함수에는 번호가 쓰여지는 번호를 nbase 하는 세 번째 매개 변수 인 nbase 가 있습니다. 
long a = strtol("101",   0, 2 ); /* a = 5L */
long b = strtol("101",   0, 8 ); /* b = 65L */
long c = strtol("101",   0, 10); /* c = 101L */
long d = strtol("101",   0, 16); /* d = 257L */
long e = strtol("101",   0, 0 ); /* e = 101L */
long f = strtol("0101",  0, 0 ); /* f = 65L */
long g = strtol("0x101", 0, 0 ); /* g = 257L */
 nbase 대한 특수 값 0 은 문자열이 C 프로그램에서 숫자 리터럴이 해석되는 것과 같은 방식으로 해석된다는 것을 의미합니다. 접두사 0x 는 16 진수 표현에 해당하고, 그렇지 않으면 선행 0 은 8 진수이며 다른 모든 숫자는 10 진수로 표시됩니다. 
따라서 명령 줄 인수를 숫자로 해석하는 가장 실용적인 방법은
int main(int argc, char* argv[] {
    if (argc < 1)
        return EXIT_FAILURE; /* No number given. */
    /* use strtoull because size_t may be wide */
    size_t mySize = strtoull(argv[1], 0, 0);
    /* then check conversion results. */
     ...
    return EXIT_SUCCESS;
}
즉, 프로그램을 8 진수, 10 진수 또는 16 진수로 매개 변수와 함께 호출 할 수 있습니다.