나는 것 완전히 다른 접근 방식을 제공하는 것 : 입력 문자열을 가져 와서 직접 tokenize 한 다음 boost::lexical_cast<T>
을 사용하여 개별 필드를 변환하십시오.
이유 : 나는 2 개의 int 및 2 개의 double 필드가 포함 된 문자열을 공백으로 구분하여 구문 분석하는 데 오후를 낭비했습니다.
int i, j;
double x, y;
std::istringstream ins{str};
ins >> i >> j >> x >> y;
// how to check errors???...
이
제대로
`"5 3 9.9e+01 5.5e+02"`
등 정확한 입력을 구문 분석 만이 가진 문제를 감지하지 않습니다 :
`"5 9.6e+01 5.5e+02"`
무엇 발생하는 i
설정 될 것입니다 다음을 수행 ~ 5 (OK)이면 j
은 9 (??), x
에서 6.0 (= 0.6e + 01), y
에서 550 (OK)으로 설정됩니다. failbit
이 설정되지 않은 것을보고 놀랐습니다. (플랫폼 정보 : OS X 10.9, Apple Clang ++ 6.0, C++ 11 모드).
당연히 이제는 "표준이 그렇게해야한다고 말하면서"라고 말하면 좋겠지 만 버그가 아니라 기능이라는 것을 알고 있으면 원하는 경우 고통을 줄일 수 있습니다 코드의 수천을 쓰지 않고도 적절한 오류 검사를 할 수 있습니다.
OTOH, "Marius"의 우수 tokeniser function을 사용하고 공백에 처음으로 str
을 분할하면 갑자기 모든 것이 매우 쉽게됩니다. 다음은 토큰 처리기의 약간 수정 된 버전입니다. 문자열의 벡터를 반환하기 위해 다시 썼습니다. 원본은 문자열로 변환 가능한 요소가있는 컨테이너에 토큰을 넣는 템플릿입니다.
std::vector<std::string> tokens = tokenizer(str, " \t", true);
if (tokens.size() < 4)
throw ParseError{"Too few fields in " + str};
try {
unsigned int i{ boost::lexical_cast<unsigned int>(tokens[0]) },
j{ boost::lexical_cast<unsigned int>(tokens[1]) };
double x{ boost::lexical_cast<double>(tokens[2]) },
y{ boost::lexical_cast<double>(tokens[3]) };
// print or process i, j, x, y ...
} catch(const boost::bad_lexical_cast& error) {
throw ParseError{"Could not parse " + str};
}
주 : (일부 예외 객체가 ParseError
)
// \param str: the input string to be tokenized
// \param delimiters: string of delimiter characters
// \param trimEmpty: if true then empty tokens will be trimmed
// \return a vector of strings containing the tokens
std::vector<std::string> tokenizer(
const std::string& str,
const std::string& delimiters = " ",
const bool trimEmpty = false
) {
std::vector<std::string> tokens;
std::string::size_type pos, lastPos = 0;
const char* strdata = str.data();
while(true) {
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos) {
// no more delimiters
pos = str.length();
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
break;
} else {
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
}
lastPos = pos + 1;
}
return tokens;
}
을 (. 이러한 일반적인 접근 방식을 필요로하는 사람들을 위해 위의 원래의 링크를 참조하시기 바랍니다) 후 바로 다음과 같이 사용하실 수 있습니다 원한다면 Boost split 또는 tokenizer을 사용하십시오.하지만 적어도 제 환경에서는 Marius의 토큰 화가보다 느립니다. 대신 boost::lexical_cast<T>
대신 "std::sto*
"기능 (예 : stoi
)을 사용하여 문자열 토큰을 int로 변환 할 수 있습니다. 이들은 변환을 수행 할 수없는 경우 std::invalid_argument
과 변환 된 값을 표현할 수없는 경우 std::out_of_range
의 두 가지 예외를 throw합니다. 별도로 또는 부모 std::runtime_error
을 잡을 수도 있습니다. 위의 예제 코드를 수정하면 독자에게 연습 문제로 남아 있습니다 :-)
답이 아닙니다. 단지 메모 만 들어 있습니다 : 't'형식의 tmp 변수를 사용하는 것이 좋습니다. case'e.eof()'는 거짓이다. – Mene
'i.peek()'를하면'EOF'가 리턴됩니까? –