1#ifndef NEFORCE_CORE_STRING_FORMAT_HPP__
2#define NEFORCE_CORE_STRING_FORMAT_HPP__
15NEFORCE_BEGIN_NAMESPACE__
74NEFORCE_CONSTEXPR20
string uint_to_string_base(
uint64_t value,
const int base,
const bool uppercase) {
82 constexpr auto digits_lower =
"0123456789abcdef";
83 constexpr auto digits_upper =
"0123456789ABCDEF";
84 const auto* digits = uppercase ? digits_upper : digits_lower;
87 const uint64_t remainder = value % base;
89 result.push_back(digits[remainder]);
96constexpr format_align to_number_alignment(
const char c) {
99 return format_align::LEFT;
101 return format_align::RIGHT;
103 return format_align::CENTER;
105 return format_align::NUMERIC;
107 return format_align::DEFAULT;
111constexpr format_type to_number_type(
const char c) {
114 return format_type::DECIMAL;
117 return format_type::BINARY;
119 return format_type::OCTAL;
122 return format_type::HEX;
125 return format_type::SCIENTIFIC;
128 return format_type::FIXED;
131 return format_type::GENERAL;
133 return format_type::CHAR;
135 return format_type::DEFAULT;
139constexpr format_options parse_number_format(
const string_view& fmt_str) {
140 format_options options;
143 if (fmt_str.empty()) {
147 bool found_align =
false;
148 if (pos + 1 < fmt_str.size()) {
149 const char first_char = fmt_str[pos];
150 const char second_char = fmt_str[pos + 1];
152 if (second_char ==
'<' || second_char ==
'>' || second_char ==
'^' || second_char ==
'=') {
153 if (first_char !=
'+' && first_char !=
'-' && first_char !=
' ') {
154 options.fill = first_char;
155 options.align = to_number_alignment(second_char);
162 if (!found_align && pos < fmt_str.size()) {
163 const char c = fmt_str[pos];
164 if (c ==
'<' || c ==
'>' || c ==
'^' || c ==
'=') {
165 options.align = to_number_alignment(c);
170 if (pos < fmt_str.size()) {
171 const char c = fmt_str[pos];
173 options.show_sign =
true;
175 }
else if (c ==
' ') {
176 options.space_sign =
true;
178 }
else if (c ==
'-') {
180 if (pos < fmt_str.size()) {
181 const char next = fmt_str[pos];
182 if (next ==
'<' || next ==
'>' || next ==
'^' || next ==
'=') {
183 options.align = to_number_alignment(next);
190 if (pos < fmt_str.size() && fmt_str[pos] ==
'#') {
191 options.alternate =
true;
195 if (pos < fmt_str.size() && fmt_str[pos] ==
'0' && options.fill ==
' ' && options.align == format_align::DEFAULT) {
196 options.zero_pad =
true;
201 if (pos < fmt_str.size() &&
is_digit(fmt_str[pos])) {
203 while (pos < fmt_str.size() &&
is_digit(fmt_str[pos])) {
204 width = width * 10 + (fmt_str[pos] -
'0');
207 options.width = width;
210 if (pos < fmt_str.size() && fmt_str[pos] ==
'.') {
213 while (pos < fmt_str.size() &&
is_digit(fmt_str[pos])) {
214 precision = precision * 10 + (fmt_str[pos] -
'0');
217 options.precision = precision;
220 if (pos < fmt_str.size()) {
221 const char c = fmt_str[pos];
222 options.type = to_number_type(c);
223 if (c ==
'X' || c ==
'E' || c ==
'G' || c ==
'B') {
224 options.uppercase =
true;
233NEFORCE_CONSTEXPR20
string apply_format_options(
string raw,
const format_options& options,
234 const bool is_numeric =
false) {
235 char existing_sign =
'\0';
236 if (!raw.empty() && (raw[0] ==
'-' || raw[0] ==
'+' || raw[0] ==
' ')) {
239 existing_sign =
sign;
243 if (options.alternate && is_numeric) {
244 switch (options.type) {
245 case format_type::HEX: {
246 prefix = options.uppercase ?
"0X" :
"0x";
249 case format_type::BINARY: {
250 prefix = options.uppercase ?
"0B" :
"0b";
253 case format_type::OCTAL: {
254 if (raw.empty() || raw[0] !=
'0') {
266 if (existing_sign ==
'-') {
268 }
else if (options.show_sign) {
270 }
else if (options.space_sign) {
274 const size_t content_len = sign_str.size() + prefix.size() + raw.size();
275 const size_t target_width = (options.width > 0) ?
static_cast<size_t>(options.width) : 0;
277 const size_t pad_total = (content_len < target_width) ? target_width - content_len : 0;
280 if (align == format_align::DEFAULT) {
281 align = is_numeric ? format_align::RIGHT : format_align::LEFT;
284 if (options.zero_pad && is_numeric && align == format_align::RIGHT) {
285 align = format_align::NUMERIC;
288 if (align == format_align::NUMERIC && is_numeric) {
289 const char fill_char = options.fill;
291 result.reserve(target_width > 0 ? target_width : content_len);
294 for (
size_t i = 0; i < pad_total; ++i) {
301 const char fill_char = options.fill;
306 case format_align::LEFT: {
307 right_pad =
string(pad_total, fill_char);
310 case format_align::CENTER: {
311 const size_t left_count = pad_total / 2;
312 const size_t right_count = pad_total - left_count;
313 left_pad =
string(left_count, fill_char);
314 right_pad =
string(right_count, fill_char);
317 case format_align::RIGHT:
319 left_pad =
string(pad_total, fill_char);
325 result.reserve(target_width > 0 ? target_width : content_len);
334template <
typename T,
bool Signed>
335struct integer_formatter_impl {
336 NEFORCE_CONSTEXPR20
string operator()(
const T value,
const format_options& options)
const {
337 using UT = conditional_t<Signed, make_unsigned_t<T>, T>;
340 const UT abs_value =
is_negative ?
static_cast<UT
>(0 -
static_cast<UT
>(value)) : static_cast<UT>(value);
341 const auto compatible =
static_cast<uint64_t>(abs_value);
345 switch (options.type) {
346 case format_type::BINARY: {
347 raw = inner::uint_to_string_base(compatible, 2, options.uppercase);
350 case format_type::OCTAL: {
351 raw = inner::uint_to_string_base(compatible, 8, options.uppercase);
354 case format_type::HEX: {
355 raw = inner::uint_to_string_base(compatible, 16, options.uppercase);
358 case format_type::CHAR: {
359 return inner::apply_format_options(string(1, static_cast<char>(value)), options, false);
361 case format_type::DECIMAL:
362 case format_type::DEFAULT:
364 raw = inner::__int_to_string_dispatch(value);
365 return inner::apply_format_options(_NEFORCE move(raw), options, true);
372 return inner::apply_format_options(_NEFORCE
move(raw), options,
true);
387template <
typename Number,
typename Dummy =
void>
411 switch (options.
type) {
438 return inner::apply_format_options(_NEFORCE
move(raw), options,
true);
458 return inner::integer_formatter_impl<T, true>{}(value, options);
479 return inner::integer_formatter_impl<T, false>{}(value, options);
488 NEFORCE_CONSTEXPR20
string operator()(
const char value,
const format_options& options)
const {
489 switch (options.
type) {
494 return inner::integer_formatter_impl<int, true>{}(
static_cast<int>(value), options);
500 return inner::apply_format_options(
string(1, value), options,
false);
506 NEFORCE_CONSTEXPR20
string operator()(
const T value,
const format_options& options)
const {
516 NEFORCE_CONSTEXPR20
string operator()(
const bool value,
const format_options& options)
const {
517 switch (options.
type) {
522 return inner::integer_formatter_impl<int, false>{}(
static_cast<int>(value), options);
528 return inner::apply_format_options(value ?
"true" :
"false", options,
false);
537 NEFORCE_CONSTEXPR20
string operator()(
const string& value,
const format_options& options)
const {
542 return inner::apply_format_options(_NEFORCE
move(raw), options,
false);
561 NEFORCE_CONSTEXPR20
string operator()(
const char* value,
const format_options& options)
const {
562 if (value ==
nullptr) {
563 return inner::apply_format_options(
"nullptr", options,
false);
575 return inner::apply_format_options(
"nullptr", options,
false);
584 NEFORCE_CONSTEXPR20
string operator()(
const T* ptr,
const format_options& options)
const {
585 return inner::apply_format_options(_NEFORCE
address_string(ptr), options,
false);
594 NEFORCE_CONSTEXPR20
string operator()(
char* value,
const format_options& options)
const {
602#ifdef NEFORCE_STANDARD_20
611consteval bool validate_format_string(
const char (&fmt)[N])
noexcept {
612 size_t brace_count = 0;
613 for (
size_t i = 0; i < N - 1; ++i) {
615 if (i + 1 < N - 1 && fmt[i + 1] ==
'{') {
620 }
else if (fmt[i] ==
'}') {
621 if (i + 1 < N - 1 && fmt[i + 1] ==
'}') {
625 if (brace_count == 0) {
631 return brace_count == 0;
648template <
size_t I,
typename Tuple>
649NEFORCE_CONSTEXPR20 enable_if_t<I == tuple_size_v<Tuple>,
string>
650format_get_and_apply(
const size_t idx,
const Tuple& args,
const format_options& opts) {
651 NEFORCE_THROW_EXCEPTION(value_exception(
"Format argument index out of range"));
654template <
size_t I,
typename Tuple>
655NEFORCE_CONSTEXPR20
enable_if_t<(I < tuple_size_v<Tuple>),
string>
656format_get_and_apply(
const size_t idx,
const Tuple& args,
const format_options& opts) {
658 return formatter<decay_t<tuple_element_t<I, Tuple>>>()(_NEFORCE get<I>(args), opts);
660 return format_get_and_apply<I + 1, Tuple>(idx, args, opts);
666NEFORCE_CONSTEXPR20
void format_impl(
const string_view fmt,
size_t& pos,
string& out) {
667 while (pos < fmt.size()) {
668 if (fmt[pos] ==
'{') {
669 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'{') {
673 NEFORCE_THROW_EXCEPTION(value_exception(
"Not enough arguments"));
675 }
else if (fmt[pos] ==
'}') {
676 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'}') {
680 NEFORCE_THROW_EXCEPTION(value_exception(
"Unmatched '}'"));
697template <
typename Tuple>
698NEFORCE_CONSTEXPR20
void format_impl(
const string_view fmt,
size_t& pos,
string& out,
const Tuple& args,
700 while (pos < fmt.size()) {
701 if (fmt[pos] ==
'{') {
702 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'{') {
708 size_t end_pos = pos;
710 while (end_pos < fmt.size() && depth > 0) {
711 if (fmt[end_pos] ==
'{') {
713 }
else if (fmt[end_pos] ==
'}') {
721 NEFORCE_THROW_EXCEPTION(value_exception(
"Unmatched '{' in format string"));
728 size_t arg_idx = next_seq;
730 if (spec_str.empty()) {
732 opts = inner::parse_number_format(
"");
738 while (num_end < spec_str.size() &&
is_digit(spec_str[num_end])) {
739 arg_idx = arg_idx * 10 +
static_cast<size_t>(spec_str[num_end] -
'0');
744 opts = inner::parse_number_format(
"");
745 }
else if (rest[0] ==
':') {
746 opts = inner::parse_number_format(rest.tail(1));
748 NEFORCE_THROW_EXCEPTION(value_exception(
"Invalid format specifier"));
750 }
else if (spec_str[0] ==
':') {
752 opts = inner::parse_number_format(spec_str.tail(1));
755 NEFORCE_THROW_EXCEPTION(value_exception(
"Invalid format specifier"));
758 if (arg_idx >= tuple_size_v<Tuple>) {
759 NEFORCE_THROW_EXCEPTION(value_exception(
"Format argument index out of range"));
762 out += inner::format_get_and_apply<0, Tuple>(arg_idx, args, opts);
763 }
else if (fmt[pos] ==
'}') {
764 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'}') {
768 NEFORCE_THROW_EXCEPTION(value_exception(
"Unmatched '}' in format string"));
779template <
typename First,
typename... Rest>
780NEFORCE_CONSTEXPR20
void format_impl(
const string_view fmt,
size_t& pos,
string& out, First&& first, Rest&&... rest) {
781 const auto args = _NEFORCE
forward_as_tuple(_NEFORCE forward<First>(first), _NEFORCE forward<Rest>(rest)...);
783 inner::format_impl(fmt, pos, out, args, next_seq);
804template <
typename... Args,
enable_if_t<(
sizeof...(Args) > 0),
int> = 0>
811 inner::format_impl(fmt, pos, result, args_tuple, next_seq);
827template <
size_t N,
typename... Args, enable_if_t<(
sizeof...(Args) > 0),
int> = 0>
828NEFORCE_NODISCARD NEFORCE_CONSTEXPR20
string format(
const char (&fmt)[N], Args&&... args) {
842NEFORCE_NODISCARD NEFORCE_CONSTEXPR20
string
848 while (i < fmt.
size()) {
849 if (fmt[i] ==
'{' && i + 1 < fmt.
size()) {
850 if (fmt[i + 1] ==
'{') {
857 while (j < fmt.
size() && fmt[j] !=
'}') {
860 if (j >= fmt.
size()) {
867 for (
const auto& param: params) {
870 result += param.second;
876 result += fmt.
view(i, j - i + 1);
879 }
else if (fmt[i] ==
'}' && i + 1 < fmt.
size() && fmt[i + 1] ==
'}') {
900template <
typename... Args, enable_if_t<(
sizeof...(Args) > 0),
int> = 0>
906 format_impl(result, fmt, opts, _NEFORCE
forward<Args>(args)...);
912NEFORCE_END_NAMESPACE__
constexpr basic_string_view tail(const size_type off=0) const
获取尾部子串
constexpr size_type size() const noexcept
获取字符串长度
constexpr basic_string_view substr(const size_type off=0, const size_type count=npos) const
获取子视图
constexpr basic_string_view view(const size_type off, const size_type count=npos) const
获取子视图
constexpr size_type size() const noexcept
获取字符数
constexpr basic_string head(const size_type count=npos) const
获取头部子串
constexpr void reserve(const size_type n)
预留容量
string format_number(int64_t value) const
格式化整数
constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
constexpr bool is_unpackaged_v
is_unpackaged的便捷变量模板
constexpr bool is_signed_v
is_signed的便捷变量模板
constexpr bool is_floating_point_v
is_floating_point的便捷变量模板
constexpr bool is_unsigned_v
is_unsigned的便捷变量模板
constexpr bool is_standard_integral_v
is_standard_integral的便捷变量模板
constexpr bool is_base_of_v
is_base_of的便捷变量模板
constexpr bool is_digit(const CharT c) noexcept
检查字符是否为数字
unsigned long uint64_t
64位无符号整数类型
unsigned char uint8_t
8位无符号整数类型
decltype(nullptr) nullptr_t
空指针类型
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
constexpr int sign(const T &value) noexcept
获取数值的符号
constexpr bool is_negative(const T x) noexcept
检查浮点数是否为负数
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
constexpr string to_string_general(T x, int precision=6)
将浮点数转换为字符串(通用格式)
constexpr string to_string_fixed(T x, int precision=6)
将浮点数转换为字符串(固定小数格式)
constexpr string to_string_scientific(T x, int precision=6)
将浮点数转换为字符串(科学计数法格式)
basic_string< char > string
字符字符串
constexpr string address_string(const void *p)
将指针转换为十六进制地址字符串
basic_string_view< char > string_view
字符字符串视图
constexpr tuple< Types &&... > forward_as_tuple(Types &&... args) noexcept
创建转发引用元组
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名