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 진수로 매개 변수와 함께 호출 할 수 있습니다.