C ++ std :: string이 특정 문자열로 시작하고 하위 문자열을 int로 변환하는지 어떻게 확인합니까?


242

C ++에서 다음 (Python 의사 코드)을 어떻게 구현합니까?

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])

(예를 들어, argv[1]이고 --foo=98, 다음 foo_value이다 98.)

업데이트 : 나는 간단한 작은 명령 줄 도구를 아주 조금 변경하는 것을보고 있기 때문에 Boost를 주저하고 있습니다. 변화).


이것도 흥미 롭습니다.
— manlio

답변:


448

다음과 같은 매개 변수 rfind가 있는 과부하를 사용하십시오 pos.

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}

누가 다른 게 필요한가요? 순수한 STL!

많은 사람들이 "접두사를 찾는 전체 문자열을 거꾸로 검색"을 의미한다고 이것을 잘못 읽었습니다. 그것은 잘못된 결과를 줄 것입니다 (예 : string("tititito").rfind("titi")2를 비교하면 == 0false를 반환 할 것입니다). 그러나 pos매개 변수 as를 전달하므로 0검색이 해당 위치 또는 이전 위치에서만 일치하도록 제한 하기 때문에 그렇게하지 않습니다 . 예를 들면 다음과 같습니다.

std::string test = "0123123";
size_t match1 = test.rfind("123");    // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)

32
이 답변은 부스트가 아닌 가장 많이 투표되어야합니다. : D STL이 이미있을 때 왜 다른 라이브러리를 사용합니까?
— Iuliu Atudosiei

@ sweisgerber.dev, 나는 당신의 첫 경합에 혼란 스럽습니다. 문자열 의 시작 부분 에있는 find경우 반환 값은 0 만됩니다 . 다른 곳에서 발견되면 0이 아닌 반환 값을 얻을 수 있으며 찾지 못하면 0이 아닌 값을 얻습니다 . 내가 옳다고 가정하면, 비표준 항목을 가져올 필요가 없기 때문에이 대답을 선호합니다 (예, Boost가 어디에나 있다는 것을 알고 있습니다. 단순한 것들에 대해서는 핵심 C ++ 라이브러리를 선호합니다). titinpos
— paxdiablo 2016 년

@ paxdiablo : 당신이 맞습니다. 실제로 시작하는지 확인 titi하지만 변환 부분이 없습니다.
— sweisgerber.dev 9

2
이것이 대부분의 컴파일러에서 최적화되었다는 증거가 있습니까? "find"또는 "rfind"최적화를 언급하는 다른 곳에서는 확인하는 반환 값을 기반으로하는 일반적인 관행이 없습니다.
— Superziyi

2
@alcoforado "rfind는 문자열의 뒤에서 시작합니다 ..."아니오, 오버로드에만 적용되는 매개 변수 rfind()는 없습니다 pos. pos매개 변수를 사용하는 오버로드를 사용하면 전체 문자열을 검색하지 않고 해당 위치 및 이전 위치 만 검색합니다. ( 매개 변수가있는 regular find()와 마찬가지로 pos해당 위치 이상을 찾습니다.) 따라서이 pos == 0답변에 표시된 것처럼을 전달 하면 문자 그대로 해당 위치에서 일치하는 항목 만 고려합니다. 그것은 이미 답변과 의견 모두에서 설명하고있었습니다.
— Arthur Tacca

188

당신은 이것을 이렇게 할 것입니다 :

std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
    foo_value = atoi(arg.substr(prefix.size()).c_str());

이를 위해 Boost.ProgramOptions와 같은 라이브러리를 찾는 것도 좋습니다.


7
이것의 가장 큰 문제는을 atoi("123xyz")반환 123하는 반면 파이썬 int("123xyz")은 예외를 던집니다.
— Tom

우리가 할 수있는 해결 방법은 sscanf ()하고 결과와 원본을 비교하여 예외 진행 여부를 결정하는 것입니다.
— Roopesh Majeti 2009

1
아니면 그냥 교체 atoi와 함께 strtol또는 strtoll우리가 입력 값에 오류 조건을 감지 할 수있는.
— Tom

1
이것이 rfind작동 최적화에 의존하는 것보다 더 나은 솔루션 입니다.
— Calmarius

143

완전성을 위해 C 방법을 언급 할 것입니다.

str원래 문자열 인 경우 substr확인하려는 하위 문자열 인 경우

strncmp(str, substr, strlen(substr))

로 시작하면가 반환 0됩니다 . 기능 및 C 헤더 파일에있는strsubstrstrncmpstrlen<string.h>

(원래 Yaseen Rauf here 게시 , 마크 업 추가)

대소 문자를 구분하지 않으려면 strnicmp대신을 사용하십시오 strncmp.

이것은 C 방법입니다 .C ++ 문자열의 경우 다음과 같은 기능을 사용할 수 있습니다.

strncmp(str.c_str(), substr.c_str(), substr.size())

9
실제로, 모든 사람들은 단지 "부스트 사용"을하는 것 같습니다. 그리고 저는 stl 또는 OS 라이브러리 버전에 대해 감사합니다
— Force Gaia

예. 그러나 문자열에 널 문자가 없다고 가정합니다. 그렇지 않은 경우 – 사용memcmp()
— Avishai Y

왜이 간단한 아름다운 솔루션 이외의 다른 것을 사용합니까?
— Adam Zahran

88

이미 Boost를 사용하고 있다면 boost string algorithms + lexical cast boost를 사용하여 수행 할 수 있습니다 .

#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>

try {    
    if (boost::starts_with(argv[1], "--foo="))
        foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
    // bad parameter
}

여기에 제공된 많은 다른 답변과 마찬가지로 이러한 종류의 접근 방식은 매우 간단한 작업에는 적합하지만 장기적으로는 명령 줄 구문 분석 라이브러리를 사용하는 것이 좋습니다. Boost는 하나 ( Boost.Program_options )를 가지고 있으며, 이미 Boost를 사용하고 있다면 의미가 있습니다.

그렇지 않으면 "c ++ 명령 줄 파서"를 검색하면 여러 가지 옵션이 나타납니다.


107
문자열 접두사 확인을 위해 큰 의존성을 얻는 것은 캐논으로 새를 쏘는 것과 같습니다.
— Tobi

150
누군가가 C ++에서 간단한 문자열 연산을 수행하는 방법을 물을 때 "부스트 사용"은 항상 잘못된 대답입니다.
— Glenn Maynard 1

90
부스트 제안
— uglycoyote

37
프로젝트에서 이미 부스트를 사용하고 있다면 여기 부스트를 사용하는 것이 좋습니다.
— Alex Che

17
대답은 "부스트를 사용하는 경우 ..."로 시작됩니다. 분명히 이것은 "... 부스트를 사용한다면"정답입니다. 그렇지 않은 경우, @Thomas에 의해 제안 볼
— NuSkooler

82

내가 사용하는 코드 :

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}

2
가장 간결하고 std :: string에만 의존하지만, 최종 substr의 끝에서 선택적이고 오도하는 argument.size ()를 제거하는 것을 제외하고.
— 벤 브라이언트

@ ben-bryant : 감사합니다. 선택 사항인지 몰랐습니다.
— Hüseyin Yağlı

16
를 사용 substr하면 불필요한 복사가 발생합니다. str.compare(start, count, substr)에 사용되는 방법 토마스의 대답은 더 효율적입니다. razvanco13의 답변 에는을 사용하여 복사를 피하는 또 다른 방법이 있습니다 std::equal.
— Felix Dombek

4
@ HüseyinYağlı Thomas uses atoi which is only for windowsHuh? atoi그 이후로 C 표준 라이브러리 함수였습니다. 사실의 점에서, atoi그것의 윈도우 특정 -이 있지만,의 (1) C가 아니라 C ++, 그리고 (2)도 C에서 사용되지 않는 때문에 (사용해야하지 때문에 bad-이다 strtol또는 다른 관련 기능 중 하나. 때문에이 atoi있다 오류 처리는 없지만 다시 C에 있습니다).
— Parthian Shot

50

아직 아무도 STL 알고리즘 / 미스 매치 기능을 사용하지 않았습니다. 이것이 true를 반환하면 prefix는 'toCheck'의 접두사입니다.

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()

전체 예제 프로그램 :

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "Will print true if 'prefix' is a prefix of string" << std::endl;
        return -1;
    }
    std::string prefix(argv[1]);
    std::string toCheck(argv[2]);
    if (prefix.length() > toCheck.length()) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "'prefix' is longer than 'string'" <<  std::endl;
        return 2;
    }
    if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
        std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
        return 0;
    } else {
        std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
        return 1;
    }
}

편집하다:

@James T. Huggett이 제안한 것처럼 std :: equal이 질문에 더 적합합니다 .A는 B의 접두사입니까? 약간 짧은 코드입니다.

std::equal(prefix.begin(), prefix.end(), toCheck.begin())

전체 예제 프로그램 :

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char **argv) {
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "Will print true if 'prefix' is a prefix of string"
              << std::endl;
    return -1;
  }
  std::string prefix(argv[1]);
  std::string toCheck(argv[2]);
  if (prefix.length() > toCheck.length()) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "'prefix' is longer than 'string'" << std::endl;
    return 2;
  }
  if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
    std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
              << '"' << std::endl;
    return 0;
  } else {
    std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
              << toCheck << '"' << std::endl;
    return 1;
  }
}

2
std :: equal을 사용하지 않는 이유는 무엇입니까?
— Brice M. Dempsey

나에게 좋은 소리. 코드도 짧을 것입니다. 나는 정답을 지금 편집해야 할 것이다 : p
— matiu

2
std::equal문자열에 사용 하면 문자열 끝을 감지하지 못하는 단점이 있으므로 접두어가 전체 문자열보다 짧은 지 수동으로 확인해야합니다. (예제 프로그램에서 올바르게 수행되었지만 위의 한 줄짜리에서는 생략되었습니다.)
— Felix Dombek

rfind에 비해 이점이 없습니까?
— Андрей Вахрушев

26

@FelixDombek의 답변 은 C argv[1]와 현 모두 "--foo"C 스트링 이라는 점을 감안할 때 최상의 솔루션입니다.

그러나 다른 답변을 보면 텍스트가 이미로 사용할 수 있다면 std::string지금까지 언급되지 않은 간단한 제로 복사, 최대 효율적인 솔루션이 존재한다는 점에 주목할 가치가 있다고 생각했습니다 .

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));

그리고 foo가 이미 문자열이라면 :

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());

6
rfind(x, 0) == 0실제로 표준으로 정의되어야합니다starts_with
— porges

1
아니요, rfind()(대신 startswith())는 매우 비효율적이므로 문자열 끝까지 검색을 계속합니다.
— ankostis

4
@ankostis rfind (x)는 실제로 x를 찾을 때까지 처음부터 끝까지 검색합니다. 그러나 rfind (x, 0)은 시작 (position = 0)에서 시작까지 검색을 시작합니다. 검색이 필요한 곳만 검색합니다. 끝까지 검색하지 않습니다.
— 익명 겁쟁이

18

C ++ 17을 사용하면 사용할 수 있습니다 std::basic_string_view및 20 ++ C와 std::basic_string::starts_with나 std::basic_string_view::starts_with.

메모리 관리와 관련 std::string_view하여 이점은 std::string"문자열"(문자열 객체의 연속 된 시퀀스)에 대한 포인터 만 보유하고 크기를 알고 있다는 것입니다. 정수 값을 얻기 위해 소스 문자열을 이동 / 복사하지 않는 예 :

#include <exception>
#include <iostream>
#include <string>
#include <string_view>

int main()
{
    constexpr auto argument = "--foo=42"; // Emulating command argument.
    constexpr auto prefix = "--foo=";
    auto inputValue = 0;

    constexpr auto argumentView = std::string_view(argument);
    if (argumentView.starts_with(prefix))
    {
        constexpr auto prefixSize = std::string_view(prefix).size();
        try
        {
            // The underlying data of argumentView is nul-terminated, therefore we can use data().
            inputValue = std::stoi(argumentView.substr(prefixSize).data());
        }
        catch (std::exception & e)
        {
            std::cerr << e.what();
        }
    }
    std::cout << inputValue; // 42
}

1
@RolandIllig 아니오, std::atoi완전히 괜찮습니다. 잘못된 입력 (이 코드에서 처리됨)에서 예외가 발생합니다. 다른 것을 염두에 두셨습니까?
— Roi Danton

에 대해 이야기 atoi하고 <cstdlib>있습니까? 문서를 말한다 "는 예외를 throw하지 않습니다."
— Roland Illig

@RolandIllig 나는 당신의 첫 번째 의견을 말하고 있습니다. 당신은 atoi대신에 실수로 이야기하고있는 것 같습니다 std::atoi. 첫 번째는 사용하기에 안전하지 않지만 후자는 괜찮습니다. 여기 코드에서 후자를 사용하고 있습니다.
— Roi Danton

std::atoi적절한 참고 문헌을 인용하여 실제로 예외를 던진다는 것을 증명하십시오 . 당신이 할 때까지 나는 완전히 다른 방식으로 행동 ::atoi하고 std::atoi행동하는 것이 매우 혼란 스러울 것이기 때문에 당신을 믿지 않습니다 .
— Roland Illig

4
@RolandIllig 지속적으로 감사합니다! 당신이 옳습니다 . std::atoi대신에 사용 된 감독 이었습니다 std::stoi. 나는 그것을 고쳤다.
— Roi Danton

12
text.substr(0, start.length()) == start

3
@GregorDoroschenko는 "문자열이 다른 문자열로 시작하는지 확인"부분에 응답합니다.
— etarion

1
std :: string을 사용하여 효율적이고 우아합니다. 나는 이것으로부터 가장 많이 배웠다.
— Michael B

1
사용하기에 적합한 한 줄짜리 라이너에 대한 추가 포인트if (one-liner)
— Adam.at. Epsonon

@Roland Illig 왜이 경우의 동작이 정의되지 않았다고 생각하십니까? substr은 en.cppreference.com/w/cpp/string/basic_string/substr
— Macsinus

11

STL을 사용하면 다음과 같이 보일 수 있습니다.

std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
  std::istringstream iss(arg.substr(prefix.size()));
  iss >> foo_value;
}

2
이어야합니다 if (prefix.size()<=arg.size() && std::equal(...)).
— Jared Grubb

10

C 구문을 사용하여 화를 낼 위험이 있으므로이 sscanf예제는 대부분의 Boost 솔루션보다 더 우아 하다고 생각합니다 . 그리고 파이썬 인터프리터가있는 곳이라면 어디에서든 연결에 대해 걱정할 필요가 없습니다!

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

int main(int argc, char **argv)
{
    for (int i = 1; i != argc; ++i) {
        int number = 0;
        int size = 0;
        sscanf(argv[i], "--foo=%d%n", &number, &size);
        if (size == strlen(argv[i])) {
            printf("number: %d\n", number);
        }
        else {
            printf("not-a-number\n");
        }
    }
    return 0;
}

다음은 솔루션이 선행 / 트레일 링 가비지를 동등한 Python 코드와 같이 올바르게 처리하고 사용 atoi하는 것 보다 더 정확하게 처리하는 예제 출력입니다 (숫자가 아닌 접미사를 잘못 무시 함).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number

7
경우 argv[i]이다 "--foo=9999999999999999999999999"(대부분 또는 모든 구현이 올바로 수행 행동해야하지만), 동작은 정의되지 않는다. 나는 가정하고있다 9999999999999999999999999 > INT_MAX.
— 키이스 톰슨

10

내가 사용 std::string::compare아래와 같은 유틸리티 메소드에 싸여 :

static bool startsWith(const string& s, const string& prefix) {
    return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}

5

gnu getopts를 사용하지 않는 이유는 무엇입니까? 다음은 기본적인 예입니다 (안전 검사 제외).

#include <getopt.h>
#include <stdio.h>

int main(int argc, char** argv)
{
  option long_options[] = {
    {"foo", required_argument, 0, 0},
    {0,0,0,0}
  };

  getopt_long(argc, argv, "f:", long_options, 0);

  printf("%s\n", optarg);
}

다음 명령의 경우 :

$ ./a.out --foo=33

당신은 얻을 것이다

33

5

C ++ 11 호환성이 필요하고 부스트를 사용할 수없는 경우 사용 예제가 포함 된 부스트 호환 드롭 인이 있습니다.

#include <iostream>
#include <string>

static bool starts_with(const std::string str, const std::string prefix)
{
    return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}

int main(int argc, char* argv[])
{
    bool usage = false;
    unsigned int foos = 0; // default number of foos if no parameter was supplied

    if (argc > 1)
    {
        const std::string fParamPrefix = "-f="; // shorthand for foo
        const std::string fooParamPrefix = "--foo=";

        for (unsigned int i = 1; i < argc; ++i)
        {
            const std::string arg = argv[i];

            try
            {
                if ((arg == "-h") || (arg == "--help"))
                {
                    usage = true;
                } else if (starts_with(arg, fParamPrefix)) {
                    foos = std::stoul(arg.substr(fParamPrefix.size()));
                } else if (starts_with(arg, fooParamPrefix)) {
                    foos = std::stoul(arg.substr(fooParamPrefix.size()));
                }
            } catch (std::exception& e) {
                std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
                usage = true;
            }
        }
    }

    if (usage)
    {
        std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
        std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
        std::cerr << "  -f, --foo=N   use N foos (optional)" << std::endl;
        return 1;
    }

    std::cerr << "number of foos given: " << foos << std::endl;
}

2

당신은 또한 사용할 수 있습니다 strstr:

if (strstr(str, substr) == substr) {
    // 'str' starts with 'substr'
}

그러나 문자열이 실제로 'substr'로 시작하지 않으면 전체 문자열을 반복해야하기 때문에 짧은 문자열에만 유용하다고 생각합니다.


2

왜 라이브러리와 물건을 복잡하게 사용합니까? C ++ String 객체는 [] 연산자를 오버로드하므로 문자를 비교할 수 있습니다. 디렉토리에있는 모든 파일을 나열하고 보이지 않는 파일과 .. 및. 의사 파일.

while ((ep = readdir(dp)))
{
    string s(ep->d_name);
    if (!(s[0] == '.')) // Omit invisible files and .. or .
        files.push_back(s);
}

그렇게 간단합니다 ..



2
@robertwb Google+는 더 이상 사용할 수 없습니다
— _Static_assert

0
std::string text = "--foo=98";
std::string start = "--foo=";

if (text.find(start) == 0)
{
    int n = stoi(text.substr(start.length()));
    std::cout << n << std::endl;
}

3
코드 설명없이 코드를 붙여 넣지 않으면 좋을 것입니다. 감사합니다.
— Reborn

1
비효율적 인 코드는 문자열의 시작 부분부터 계속 검색합니다.
— ankostis

0

11 사용할 수있는 높은 C ++로 find()및find_first_of()

find를 사용하여 단일 문자를 찾는 예 :

#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
    // Found string containing 'a'
}

find를 사용하여 전체 문자열을 찾고 위치 5에서 시작하는 예 :

std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
    // Found string containing 'h'
}

find_first_of()첫 번째 문자 만 사용하여 시작시에만 검색하는 예제 :

std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
    // Found '.' at first position in string
}

행운을 빕니다!


왜 rfind하지? rfind (str, 0)은 전진 할 수 없으므로 선택하기 위해 전체 문자열을 불필요하게 스캔하지 않습니다. 다른 사람을보십시오.
— user2864740

0

C ++ 11 std::regex_search을 사용하면 더 복잡한 표현식 일치를 제공 할 수도 있습니다. 다음 예제는 부동 소수 점수 std::stof와 이후의 캐스트도 처리합니다 int.

그러나 접두사가 일치하지 않으면 parseInt아래에 표시된 메소드에서 std::invalid_argument예외가 발생할 수 있습니다 . 주어진 응용 프로그램에 따라 쉽게 조정할 수 있습니다.

#include <iostream>
#include <regex>

int parseInt(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
  return std::stof(match[1]);
}

int main() {
    std::cout << parseInt("foo=13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-.9", "foo=") << std::endl;
    std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
    std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
    std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;

//    throw std::invalid_argument
//    std::cout << parseInt("foo=1", "bar=") << std::endl;

    return 0;
}

정규식 패턴의 마법 종류는 다음 답변에 자세히 설명되어 있습니다.

편집 : 이전 답변은 정수로 변환하지 않았습니다.


0

C ++ 20부터는 starts_with메소드를 사용할 수 있습니다 .

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}

-3
if(boost::starts_with(string_to_search, string_to_look_for))
    intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));

이것은 완전히 테스트되지 않았습니다. 원리는 파이썬과 같습니다. Boost.StringAlgo 및 Boost.LexicalCast가 필요합니다.

문자열이 다른 문자열로 시작하는지 확인한 다음 첫 번째 문자열의 하위 문자열 ( '슬라이스')을 가져와 어휘 캐스트를 사용하여 변환하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.