std::from_chars / to_chars 고성능 문자열 변환
std::from_chars와 std::to_chars는 C++17에서 도입된 저수준 고성능 문자열 변환 함수입니다. 로케일 독립적이고 동적 메모리 할당이 없으며, 예외도 던지지 않아 파서, 직렬화, 로깅 등 성능 민감한 경로에 적합합니다.
1. from_chars — 문자열 → 숫자
섹션 제목: “1. from_chars — 문자열 → 숫자”#include <charconv>#include <string_view>#include <cassert>
int main(){ std::string_view sv = "12345"; int result{};
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
if (ec == std::errc{}) // 성공: result == 12345, ptr == sv.end() assert(result == 12345); else if (ec == std::errc::invalid_argument) // 숫자가 아닌 입력 ; else if (ec == std::errc::result_out_of_range) // 오버플로 ;}2. to_chars — 숫자 → 문자열
섹션 제목: “2. to_chars — 숫자 → 문자열”#include <charconv>#include <array>#include <string_view>
int main(){ int value = 42; std::array<char, 20> buf;
auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), value);
if (ec == std::errc{}) { std::string_view sv(buf.data(), ptr); // sv == "42" }}3. 진수 변환
섹션 제목: “3. 진수 변환”#include <charconv>#include <array>
int parse_hex(std::string_view s){ int result{}; std::from_chars(s.data(), s.data() + s.size(), result, 16); return result;}
// "ff" → 255, "1A" → 26
std::string to_hex(int v){ std::array<char, 20> buf; auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), v, 16); return std::string(buf.data(), ptr);}// 255 → "ff"4. 부동소수점 변환 (C++17)
섹션 제목: “4. 부동소수점 변환 (C++17)”#include <charconv>#include <array>
double parse_double(std::string_view s){ double result{}; std::from_chars(s.data(), s.data() + s.size(), result); return result;}
// 부동소수점 출력 형식 제어std::string format_double(double v){ std::array<char, 32> buf; // 고정 소수점 6자리 auto [ptr, ec] = std::to_chars( buf.data(), buf.data() + buf.size(), v, std::chars_format::fixed, 6); return std::string(buf.data(), ptr);}// 3.14159265 → "3.141593"std::chars_format 옵션:
fixed— 고정 소수점scientific— 과학적 표기 (1.23e+04)hex— 16진수 부동소수점general— 더 짧은 형식 자동 선택
5. 기존 방법과 성능 비교
섹션 제목: “5. 기존 방법과 성능 비교”| 함수 | 로케일 의존 | 예외 | 동적 할당 | 상대 속도 |
|---|---|---|---|---|
atoi | ✗ | ✗ | ✗ | 기준 |
stoi | ✓ | ✓ | ✓ | 느림 |
sscanf | ✓ | ✗ | ✗ | 느림 |
from_chars | ✗ | ✗ | ✗ | 빠름 (~3×) |
6. 실전 패턴 — CSV 파서
섹션 제목: “6. 실전 패턴 — CSV 파서”#include <charconv>#include <string_view>#include <vector>
std::vector<int> parse_csv_ints(std::string_view line){ std::vector<int> result; const char* ptr = line.data(); const char* end = ptr + line.size();
while (ptr < end) { int val{}; auto [next, ec] = std::from_chars(ptr, end, val);
if (ec == std::errc{}) result.push_back(val);
ptr = next; while (ptr < end && (*ptr == ',' || *ptr == ' ')) ++ptr; // 구분자 건너뛰기 } return result;}
// "1, 2, 3, 42" → {1, 2, 3, 42}7. 유틸리티 래퍼
섹션 제목: “7. 유틸리티 래퍼”#include <charconv>#include <optional>#include <string_view>
template<typename T>std::optional<T> parse(std::string_view s) noexcept{ T result{}; auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), result); if (ec == std::errc{} && ptr == s.data() + s.size()) return result; return std::nullopt;}
auto v = parse<int>("123"); // optional{123}auto bad = parse<int>("12x"); // nullopt (전체 소비 안됨)8. Round-Trip 보장
섹션 제목: “8. Round-Trip 보장”to_chars + from_chars로 부동소수점 값을 손실 없이 직렬화/역직렬화할 수 있습니다.
#include <charconv>#include <array>#include <cassert>
double original = 3.141592653589793;
// to_chars 기본값(chars_format::general): round-trip 보장 형식std::array<char, 32> buf;auto [end_ptr, ec1] = std::to_chars(buf.data(), buf.data() + buf.size(), original);// buf에 "3.141592653589793" 저장
// 역변환double restored;auto [_, ec2] = std::from_chars(buf.data(), end_ptr, restored);
assert(ec1 == std::errc{} && ec2 == std::errc{});assert(original == restored); // 비트 단위 동일 보장9. 버퍼 크기 계산
섹션 제목: “9. 버퍼 크기 계산”// 안전한 버퍼 크기 공식// 정수: std::numeric_limits<T>::digits10 + 3 (부호, 소수점, null)constexpr int INT_BUF = std::numeric_limits<int>::digits10 + 3; // 12constexpr int LONG_BUF = std::numeric_limits<long long>::digits10 + 3; // 22
// 부동소수점: max_digits10 + 부호 + 소수점 + 지수부constexpr int DBL_BUF = std::numeric_limits<double>::max_digits10 + 10; // 27
std::array<char, DBL_BUF> dbuf;auto [ptr, ec] = std::to_chars(dbuf.data(), dbuf.data() + dbuf.size(), 1.23456789e100);// "1.23456789e+100" — 버퍼 크기 충분from_chars/to_chars는 파서, 직렬화, 로깅처럼 많은 양의 숫자 변환이 필요한 경로에서 stoi/to_string의 로케일 오버헤드와 예외 비용을 제거합니다. 기본 포맷(general)으로 to_chars를 사용하면 round-trip이 보장되어 JSON, 바이너리 프로토콜 직렬화에 안전합니다. std::errc로 오류를 값으로 처리하고 동적 할당이 전혀 없으므로 임베디드나 게임 서버 같은 성능 민감한 환경에서도 안전하게 사용할 수 있습니다.