C++ string 다루기 3

2 분 소요

std::string 리터럴

코드에서 주로 사용하는 스트링 리터를은 const char*로 처리한다. 표준 사용자 정의 리터럴 's'를 사용하면 스트링 리터럴을 std::string으로 만들 수 있다.
auto string1 = "Hello World";   // string1의 타입은 const char* 이다.
auto string2 = "Hello World"s;  // string2의 타입은 std::string이다.
표준 사용자 정의 리터럴 's'를 사용하려면
  • using namespace std::string_literals; 또는
  • using namespace std를 추가한다.
s는 다음과같이 operator""s로 정의되어 있다.

하이레벨 숫자 변환

std 네임스페이스에는 숫자와 string사이를 쉽게 변환할 수 있도록 하는 다양한 함수가 존재한다.
std::string to_string(int val);
std::string to_string(unsigned long long val);
...
std::string to_string(double val);
위 함수들은 string 객체를 새로 생성해서 리턴한다.

반대로 변환하는 함수들도 std 네임스페이스에 정의되어 있다.
int stoi(const string& str, size_t *idx=0, int base=10);
...
double stod(const string& str, size_t *idx=0);
idx는 아직 변환되지 않은 부분의 맨 앞에 있는 문자의 인덱스를 가리키는 포인터이고, base는 변환할 수의 밑(base)이다. idx를 nullptr로 지정하면 이 값을 무시하게 된다. 예를들어 아래와 같은 코드를 보게 되면
const std::string toParse = " 123USD";
size_t index = 0;
int value = std::stoi(toParse, &index);
std::cout << "Parsed value: " << value << std::endl;
std::cout << "First non-parsed character: '" << toParse[index] << "'" << std::endl;
다음과 같은 결과를 확인할 수 있다.
NOTE_ 코드 실행 결과
Parsed value: 123
First non-parsed character: 'U'

로우 레벨 숫자 변환

C++17부터 로우 레벨 숫자 변환에 대한 함수도 다양하게 제공된다. 이 함수는 <charconv>헤더에 정의돼 있다. 이 함수는 메모리 할당에 관련된 작업은 전혀 해주지 않기 때문에 호출한 측에서 버퍼를 할당하는 방식으로 사용해야 한다.
다른 하이 레벨 숫자 변환 함수에 비해 처리 속도가 엄청나게 빠르기 때문에 숫자 데이터와 사람이 읽기 좋은 포맷(JSON, XML등) 사이의 변환 작업을 로케일에 독립적이면서 빠른 속도로 처리하고 싶다면 이러한 로우 레벨 함수를 사용한다. 정수를 문자로 변환하려면 다음과 같은 함수를 사용한다.
to_chars_result to_chars(char* fist, char* last, IntegerT value, int base = 10);
to_chars_result는 다음과 같이 정의돼 있다.
struct to_chars_result {
    char* ptr;
    errc ec;
}
졍상적으로 변환됐다면 ptr 멤버는 끝에서 두번째 문자를 가리키고, 그렇지 않으면 last 값과 같다. (이때 ec == errc::value_too_large다.)
아래 예를 보자
std::string out(10, ' ');
auto result = std::to_chars(out.data(), out.data() + out.size(), 12345);
if(result.ec == std::errc()){}
C++17의 구조적 바인딩을 적용하면 아래와 같이 작성할 수 있다.
std::string out(10, ' ');
auto [ptr ec] = std::to_chars(out.data(), out.data() + out.size(), 12345);
if(ec == std::errc()){}
물론 부동소수점 타입에 대한 변환 함수도 제공한다.
to_chars_result to_chars(char* first, char* last, FloatT value);
to_chars_result to_chars(char* first, char* last, FloatT value, chars_format format);
to_chars_result to_chars(char* first, char* last, FloatT value, chars_format format, int precision);
구체적인 포멧은 chars_format플래그를 조합한다.
enum class chars_format {
    scientific,                     // 스타일: (-)d, ddde +-dd
    fixed,                          // 스타일: (-)ddd.ddd
    hex,                            // 스타일: (-)h.hhhp +-d(주의: 0x는 적지 않는다.)
    general = fixed | scientific    // ?
}
  • 기본 포맷인 char_format::general을 적용하면 to_chars()는 부동소수점값을 십진수 표기법인 (-)ddd.ddd와 지수 표기법인 (-)d.ddde+-dd 중에서 전체 길이가 짧은 형태로 변환된다.
  • 포맷에 정밀도(precision)를 지정하지 않으면 주어진 포맷에서 가장 짧게 표현할 수 있는 형태로 결정된다.
  • 정밀도의 최댓값은 여섯자리다.
반대방으로의 변환도 있다.
from_chars_result from_chars(const char* first, const char* last, IntegerT& value, int base = 10);
from_chars_result from_chars(const char* first, const char* last, FloatT& value, chars_format format = chars_format::general);
반환형인 from_chars_result는 다음과 같이 정의되어 있다.
struct from_chars_result {
    const char* ptr;
    errc ec;
}
  • ptr 멤버는 변환에 실패할 경우 첫 번째 문자에 대한 포인터가 된다. 제대로 변환될 경우 last와 같다.
  • 변환된 문자가 하나도 없다면 ptr는 first와 같고, 에러코드는 errc::invalid_argument가 된다.
  • 파싱된 값이 너무 커서 표현할 수 없다면 에러 코드 값은 errc::result_out_of_range가 된다.
  • from_chars()는 앞에 나온 공백 문자를 무시하지 않는다.

참고

  • 전문가를 위한 C++