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_withstd::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.