[C++20 새기능 소개] std::format 라이브러리

C++20의 새로운 기능들을 소개하는 시리즈의 여덟 번째 글에 오신 것을 환영합니다. 이번 글에서는 문자열 포맷팅을 더욱 편리하고 안전하게 만들어 줄 std::format 라이브러리에 대해 자세히 알아보겠습니다.

std::format이란 무엇인가요?

C++20에서 도입된 std::format 라이브러리는 문자열을 포맷팅하는 새로운 방법을 제공합니다. 이는 C++에서 안전하고 간편하게 문자열을 생성할 수 있도록 설계되었으며, Python의 f-string이나 format() 함수와 유사한 기능을 제공합니다.

왜 std::format을 사용해야 할까요?

기존의 문자열 포맷팅 방식인 printf 계열 함수나 std::ostringstream 등을 사용할 때는 타입 안전성이나 가독성 측면에서 한계가 있었습니다. std::format은 이러한 문제를 해결하고, 더욱 직관적이고 안전한 문자열 포맷팅을 가능하게 합니다.

간단한 예제

기존 방식의 문자열 포맷팅

#include <iostream>
#include <string>

int main() {
    int age = 30;
    std::string name = "Alice";

    // printf 사용
    printf("%s의 나이는 %d세입니다.\n", name.c_str(), age);

    // ostringstream 사용
    std::ostringstream oss;
    oss << name << "의 나이는 " << age << "세입니다.\n";
    std::cout << oss.str();

    return 0;
}

위 코드에서는 printf와 ostringstream을 사용하여 문자열을 포맷팅했습니다.

std::format을 사용한 개선

#include <iostream>
#include <format>

int main() {
    int age = 30;
    std::string name = "Alice";

    std::string message = std::format("{}의 나이는 {}세입니다.\n", name, age);
    std::cout << message;

    return 0;
}

std::format을 사용하면 더욱 간결하고 가독성 높은 코드를 작성할 수 있습니다.

std::format의 사용 방법

1. 기본 사용법

std::string result = std::format("Hello, {}!", "World");
std::cout << result; // 출력: Hello, World!
  • 중괄호 {}를 사용하여 포맷팅할 위치를 지정합니다.
  • 위치에 해당하는 인자를 순서대로 전달합니다.

2. 위치 인덱스 사용

std::string result = std::format("{1}님, {0}을 확인해주세요.", "메시지", "홍길동");
std::cout << result; // 출력: 홍길동님, 메시지를 확인해주세요.
  • {n} 형식으로 위치 인덱스를 지정하여 인자의 순서를 조정할 수 있습니다.

3. 형식 지정자 사용

double pi = 3.1415926535;
std::string result = std::format("원주율은 {:.2f}입니다.", pi);
std::cout << result; // 출력: 원주율은 3.14입니다.
  • :{} 안에 형식 지정자를 사용하여 출력 형식을 지정합니다.
  • .2f는 소수점 아래 두 자리까지 표시하는 부동소수점 형식을 의미합니다.

4. 정렬 및 채우기

std::string result = std::format("[{:*>10}]", "Test");
std::cout << result; // 출력: [******Test]
  • >는 오른쪽 정렬을 의미하며, *는 빈 공간을 채울 문자를 지정합니다.
  • 10은 총 필드 너비를 나타냅니다.

5. 다양한 데이터 타입 포맷팅

#include <chrono>

int main() {
    auto now = std::chrono::system_clock::now();
    std::string datetime = std::format("현재 시간: {:%Y-%m-%d %H:%M:%S}", now);
    std::cout << datetime << '\n';

    std::complex<double> c{3.0, 4.0};
    std::string complex_str = std::format("복소수: {}", c);
    std::cout << complex_str << '\n';

    return 0;
}
  • 날짜와 시간, 복소수 등 다양한 타입에 대해 포맷팅이 가능합니다.

std::format의 장점

  • 타입 안전성: 컴파일 타임에 인자 타입을 검사하여 오류를 방지합니다.
  • 가독성 향상: 코드가 간결하고 이해하기 쉽습니다.
  • 유연한 포맷팅: 다양한 형식 지정자를 통해 세밀한 제어가 가능합니다.
  • 국제화 지원: 지역화된 포맷팅을 지원하여 국제화에 유용합니다.

주의 사항

  • 컴파일러 지원: std::format은 C++20 표준의 일부이므로, 이를 지원하는 컴파일러와 표준 라이브러리가 필요합니다. CMake 설정 하는 법 참고.
  • 예외 처리: 포맷팅 중 오류가 발생하면 std::format_error 예외가 발생하므로, 예외 처리를 고려해야 합니다.

더 다양한 예제

1. 숫자 형식 지정

int number = 255;
std::cout << std::format("10진수: {0}, 16진수: {0:#x}, 8진수: {0:#o}\n", number);
// 출력: 10진수: 255, 16진수: 0xff, 8진수: 0377
  • #x는 16진수 형식, #o는 8진수 형식을 나타냅니다.

2. bool 값 포맷팅

bool flag = true;
std::cout << std::format("플래그 상태: {}\n", flag);
// 출력: 플래그 상태: true
  • bool 값은 true 또는 false로 출력됩니다.

3. 너비와 정밀도 지정

double value = 123.456789;
std::cout << std::format("값: {0:10.2f}\n", value);
// 출력: 값:     123.46
  • 10.2f는 총 10자리 필드 너비에 소수점 아래 두 자리까지 표시하는 부동소수점 형식을 의미합니다.

4. 지역화된 포맷팅

#include <locale>

int main() {
    double number = 1234567.89;
    std::locale::global(std::locale("en_US.UTF-8"));

    std::cout << std::format(std::locale(), "지역화된 숫자: {:L}\n", number);
    // 출력: 지역화된 숫자: 1,234,567.89

    return 0;
}
  • {:L}은 현재 로케일에 따라 숫자를 포맷팅합니다.

std::format과 fmt 라이브러리

  • std::format은 fmt 라이브러리를 기반으로 합니다.
  • fmt 라이브러리는 C++11 이상에서 사용할 수 있는 오픈 소스 라이브러리로, std::format과 호환됩니다.
  • C++20을 지원하지 않는 환경에서는 fmt 라이브러리를 사용할 수 있습니다.
// fmt 라이브러리 사용 예제
#include <fmt/core.h>

int main() {
    std::string message = fmt::format("안녕하세요, {}님!", "홍길동");
    fmt::print("{}\n", message);
    return 0;
}

결론

C++20의 std::format 라이브러리는 문자열 포맷팅을 더욱 쉽고 안전하게 만들어 줍니다. 이를 통해 복잡한 문자열 조작을 간단하고 가독성 높게 구현할 수 있으며, 다양한 데이터 타입과 포맷 옵션을 지원하여 유연한 코딩이 가능합니다.

 

참고 자료:

 

반응형