NexusForce 1.0.0
A Modern C++ Library with extended functionality, web components, and utility libraries
载入中...
搜索中...
未找到
format.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_STRING_FORMAT_HPP__
2#define NEFORCE_CORE_STRING_FORMAT_HPP__
3
11
13NEFORCE_BEGIN_NAMESPACE__
14
20
32
48
56 char fill = ' ';
59 int width = 0;
60 int precision = -1;
61 bool uppercase = false;
62 bool alternate = false;
63 bool zero_pad = false;
64 bool show_sign = false;
65 bool space_sign = false;
66};
67
69NEFORCE_BEGIN_INNER__
70
71constexpr format_align to_number_alignment(const char c) {
72 switch (c) {
73 case '<':
74 return format_align::LEFT;
75 case '>':
77 case '^':
79 case '=':
81 default:
83 }
84}
85
86constexpr format_type to_number_type(const char c) {
87 switch (c) {
88 case 'd':
90 case 'b':
92 case 'B':
94 case 'o':
95 return format_type::OCTAL;
96 case 'x':
97 return format_type::HEX;
98 case 'X':
99 return format_type::HEX;
100 case 'e':
102 case 'E':
104 case 'f':
105 return format_type::FIXED;
106 case 'F':
107 return format_type::FIXED;
108 case 'g':
110 case 'G':
112 case 'c':
113 return format_type::CHAR;
114 default:
116 }
117}
118
119constexpr format_options parse_number_format(const string_view& fmt_str) {
120 format_options options;
121 size_t pos = 0;
122
123 if (fmt_str.empty()) {
124 return options;
125 }
126
127 bool found_align = false;
128 if (pos + 1 < fmt_str.size()) {
129 const char first_char = fmt_str[pos];
130 const char second_char = fmt_str[pos + 1];
131
132 if (second_char == '<' || second_char == '>' || second_char == '^' || second_char == '=') {
133 if (first_char != '+' && first_char != '-' && first_char != ' ') {
134 options.fill = first_char;
135 options.align = to_number_alignment(second_char);
136 pos += 2;
137 found_align = true;
138 }
139 }
140 }
141
142 if (!found_align && pos < fmt_str.size()) {
143 const char c = fmt_str[pos];
144 if (c == '<' || c == '>' || c == '^' || c == '=') {
145 options.align = to_number_alignment(c);
146 ++pos;
147 }
148 }
149
150 if (pos < fmt_str.size()) {
151 const char c = fmt_str[pos];
152 if (c == '+') {
153 options.show_sign = true;
154 ++pos;
155 } else if (c == ' ') {
156 options.space_sign = true;
157 ++pos;
158 } else if (c == '-') {
159 ++pos;
160 if (pos < fmt_str.size()) {
161 const char next = fmt_str[pos];
162 if (next == '<' || next == '>' || next == '^' || next == '=') {
163 options.align = to_number_alignment(next);
164 ++pos;
165 }
166 }
167 }
168 }
169
170 if (pos < fmt_str.size() && fmt_str[pos] == '#') {
171 options.alternate = true;
172 ++pos;
173 }
174
175 if (pos < fmt_str.size() && fmt_str[pos] == '0' && options.fill == ' ' && options.align == format_align::DEFAULT) {
176 options.zero_pad = true;
177 options.fill = '0';
178 ++pos;
179 }
180
181 if (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
182 int width = 0;
183 while (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
184 width = width * 10 + (fmt_str[pos] - '0');
185 ++pos;
186 }
187 options.width = width;
188 }
189
190 if (pos < fmt_str.size() && fmt_str[pos] == '.') {
191 ++pos;
192 int precision = 0;
193 while (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
194 precision = precision * 10 + (fmt_str[pos] - '0');
195 ++pos;
196 }
197 options.precision = precision;
198 }
199
200 if (pos < fmt_str.size()) {
201 const char c = fmt_str[pos];
202 options.type = to_number_type(c);
203 if (c == 'X' || c == 'E' || c == 'G' || c == 'B') {
204 options.uppercase = true;
205 }
206 ++pos;
207 }
208
209 return options;
210}
211
212
213NEFORCE_CONSTEXPR20 string apply_format_options(string raw, const format_options& options,
214 const bool is_numeric = false) {
215 char existing_sign = '\0';
216 if (!raw.empty() && (raw[0] == '-' || raw[0] == '+' || raw[0] == ' ')) {
217 char sign = raw[0];
218 raw = raw.substr(1);
219 existing_sign = sign;
220 }
221
222 string prefix;
223 if (options.alternate && is_numeric) {
224 switch (options.type) {
225 case format_type::HEX: {
226 prefix = options.uppercase ? "0X" : "0x";
227 break;
228 }
229 case format_type::BINARY: {
230 prefix = options.uppercase ? "0B" : "0b";
231 break;
232 }
233 case format_type::OCTAL: {
234 if (raw.empty() || raw[0] != '0') {
235 prefix = "0";
236 }
237 break;
238 }
239 default: {
240 break;
241 }
242 }
243 }
244
245 string sign_str;
246 if (existing_sign == '-') {
247 sign_str = "-";
248 } else if (options.show_sign) {
249 sign_str = "+";
250 } else if (options.space_sign) {
251 sign_str = " ";
252 }
253
254 const size_t content_len = sign_str.size() + prefix.size() + raw.size();
255 const size_t target_width = (options.width > 0) ? static_cast<size_t>(options.width) : 0;
256
257 const size_t pad_total = (content_len < target_width) ? target_width - content_len : 0;
258
259 format_align align = options.align;
260 if (align == format_align::DEFAULT) {
261 align = is_numeric ? format_align::RIGHT : format_align::LEFT;
262 }
263
264 if (options.zero_pad && is_numeric && align == format_align::RIGHT) {
265 align = format_align::NUMERIC;
266 }
267
268 if (align == format_align::NUMERIC && is_numeric) {
269 const char fill_char = options.fill;
270 string result;
271 result.reserve(target_width > 0 ? target_width : content_len);
272 result += sign_str;
273 result += prefix;
274 for (size_t i = 0; i < pad_total; ++i) {
275 result += fill_char;
276 }
277 result += raw;
278 return result;
279 }
280
281 const char fill_char = options.fill;
282 string left_pad;
283 string right_pad;
284
285 switch (align) {
286 case format_align::LEFT: {
287 right_pad = string(pad_total, fill_char);
288 break;
289 }
291 const size_t left_count = pad_total / 2;
292 const size_t right_count = pad_total - left_count;
293 left_pad = string(left_count, fill_char);
294 right_pad = string(right_count, fill_char);
295 break;
296 }
298 default: {
299 left_pad = string(pad_total, fill_char);
300 break;
301 }
302 }
303
304 string result;
305 result.reserve(target_width > 0 ? target_width : content_len);
306 result += left_pad;
307 result += sign_str;
308 result += prefix;
309 result += raw;
310 result += right_pad;
311 return result;
312}
313
314template <typename T, bool Signed>
315struct integer_formatter_impl {
316 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
318
319 const bool is_negative = Signed && (value < 0);
320 const UT abs_value = is_negative ? static_cast<UT>(0 - static_cast<UT>(value)) : static_cast<UT>(value);
321 const uint64_t compatible = static_cast<uint64_t>(abs_value);
322
323 string raw;
324
325 switch (options.type) {
326 case format_type::BINARY: {
327 raw = inner::__uint_to_string_base(compatible, 2, options.uppercase);
328 break;
329 }
330 case format_type::OCTAL: {
331 raw = inner::__uint_to_string_base(compatible, 8, options.uppercase);
332 break;
333 }
334 case format_type::HEX: {
335 raw = inner::__uint_to_string_base(compatible, 16, options.uppercase);
336 break;
337 }
338 case format_type::CHAR: {
339 return inner::apply_format_options(string(1, static_cast<char>(value)), options, false);
340 }
343 default: {
344 raw = inner::__int_to_string_dispatch(value);
345 return inner::apply_format_options(_NEFORCE move(raw), options, true);
346 }
347 }
348
349 if (is_negative) {
350 raw = "-" + raw;
351 }
352 return inner::apply_format_options(_NEFORCE move(raw), options, true);
353 }
354};
355
356NEFORCE_END_INNER__
358
367template <typename Number, typename Dummy = void>
369
374template <typename T>
382 NEFORCE_CONSTEXPR20 string operator()(const T& value, const format_options& options) const {
383 const int prec = (options.precision >= 0) ? options.precision : 6;
384 string raw;
385
386 switch (options.type) {
388 raw = _NEFORCE to_string_scientific(value, prec);
389 break;
390 }
391 case format_type::FIXED: {
392 raw = _NEFORCE to_string_fixed(value, prec);
393 break;
394 }
396 raw = _NEFORCE to_string_general(value, prec);
397 break;
398 }
401 default: {
402 raw = _NEFORCE to_string_general(value, prec);
403 break;
404 }
405 }
406
407 if (options.uppercase) {
408 for (auto& c: raw) {
409 if (c == 'e') {
410 c = 'E';
411 break;
412 }
413 }
414 }
415
416 return inner::apply_format_options(_NEFORCE move(raw), options, true);
417 }
418};
419
424template <typename T>
432 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
433 return inner::integer_formatter_impl<T, true>{}(value, options);
434 }
435};
436
441template <typename T>
449 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
450 return inner::integer_formatter_impl<T, false>{}(value, options);
451 }
452};
453
457template <>
458struct formatter<char> {
459 NEFORCE_CONSTEXPR20 string operator()(const char value, const format_options& options) const {
460 switch (options.type) {
463 case format_type::HEX:
465 return inner::integer_formatter_impl<int, true>{}(static_cast<int>(value), options);
466 }
467 default: {
468 break;
469 }
470 }
471 return inner::apply_format_options(string(1, value), options, false);
472 }
473};
474
475template <typename T>
477 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
478 return formatter<unpackage_t<T>>()(value.value(), options);
479 }
480};
481
485template <>
486struct formatter<bool> {
487 NEFORCE_CONSTEXPR20 string operator()(const bool value, const format_options& options) const {
488 switch (options.type) {
491 case format_type::HEX:
493 return inner::integer_formatter_impl<int, false>{}(value, options);
494 }
495 default: {
496 break;
497 }
498 }
499 return inner::apply_format_options(value ? "true" : "false", options, false);
500 }
501};
502
506template <>
508 NEFORCE_CONSTEXPR20 string operator()(const string& value, const format_options& options) const {
509 string raw = value;
510 if (options.precision >= 0 && raw.size() > static_cast<size_t>(options.precision)) {
511 raw = raw.substr(0, static_cast<size_t>(options.precision));
512 }
513 return inner::apply_format_options(_NEFORCE move(raw), options, false);
514 }
515};
516
520template <>
522 NEFORCE_CONSTEXPR20 string operator()(const string_view value, const format_options& options) const {
523 return formatter<string>()(string(value), options);
524 }
525};
526
530template <>
531struct formatter<const char*> {
532 NEFORCE_CONSTEXPR20 string operator()(const char* value, const format_options& options) const {
533 if (value == nullptr) {
534 return inner::apply_format_options("nullptr", options, false);
535 }
536 return formatter<string>{}(string(value), options);
537 }
538};
539
543template <>
545 NEFORCE_CONSTEXPR20 string operator()(nullptr_t, const format_options& options) const {
546 return inner::apply_format_options("nullptr", options, false);
547 }
548};
549
553template <typename T>
555 NEFORCE_CONSTEXPR20 string operator()(const T* ptr, const format_options& options) const {
556 return inner::apply_format_options(_NEFORCE address_string(ptr), options, false);
557 }
558};
559
563template <>
564struct formatter<char*> {
565 NEFORCE_CONSTEXPR20 string operator()(char* value, const format_options& options) const {
566 return formatter<string>()(string(value), options);
567 }
568};
569
571NEFORCE_BEGIN_INNER__
572
581NEFORCE_CONSTEXPR20 void format_impl(const string_view fmt, size_t& pos, string& out) {
582 while (pos < fmt.size()) {
583 if (fmt[pos] == '{') {
584 if (pos + 1 < fmt.size() && fmt[pos + 1] == '{') {
585 out += '{';
586 pos += 2;
587 } else {
588 NEFORCE_THROW_EXCEPTION(value_exception("Not enough arguments"));
589 }
590 } else if (fmt[pos] == '}') {
591 if (pos + 1 < fmt.size() && fmt[pos + 1] == '}') {
592 out += '}';
593 pos += 2;
594 } else {
595 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '}'"));
596 }
597 } else {
598 out += fmt[pos++];
599 }
600 }
601}
602
615template <typename First, typename... Rest>
616NEFORCE_CONSTEXPR20 void format_impl(const string_view fmt, size_t& pos, string& out, First&& first, Rest&&... rest) {
617 while (pos < fmt.size()) {
618 if (fmt[pos] == '{') {
619 if (pos + 1 < fmt.size() && fmt[pos + 1] == '{') {
620 out += '{';
621 pos += 2;
622 continue;
623 }
624 ++pos;
625 size_t end_pos = pos;
626 int depth = 1;
627 while (end_pos < fmt.size() && depth > 0) {
628 if (fmt[end_pos] == '{') {
629 ++depth;
630 } else if (fmt[end_pos] == '}') {
631 --depth;
632 }
633 if (depth > 0) {
634 ++end_pos;
635 }
636 }
637 if (depth != 0) {
638 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '{' in format string"));
639 }
640
641 const string_view spec_str = fmt.substr(pos, end_pos - pos);
642 pos = end_pos + 1;
643
644 format_options opts;
645 if (spec_str.empty()) {
646 opts = inner::parse_number_format("");
647 } else if (spec_str[0] == ':') {
648 opts = inner::parse_number_format(spec_str.substr(1));
649 } else {
650 opts = format_options{};
651 }
652 out += formatter<decay_t<First>>()(_NEFORCE forward<First>(first), opts);
653 inner::format_impl(fmt, pos, out, _NEFORCE forward<Rest>(rest)...);
654 return;
655 } else if (fmt[pos] == '}') {
656 if (pos + 1 < fmt.size() && fmt[pos + 1] == '}') {
657 out += '}';
658 pos += 2;
659 } else {
660 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '}' in format string"));
661 }
662 } else {
663 out += fmt[pos++];
664 }
665 }
666}
667
668NEFORCE_END_INNER__
670
679template <typename... Args, enable_if_t<(sizeof...(Args) > 0), int> = 0>
680NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string format(const string_view fmt, Args&&... args) {
681 string result;
682 result.reserve(fmt.size() + sizeof...(Args) * 8);
683 size_t pos = 0;
684 inner::format_impl(fmt, pos, result, _NEFORCE forward<Args>(args)...);
685 return result;
686}
687 // Format
689
690NEFORCE_END_NAMESPACE__
691#endif // NEFORCE_CORE_STRING_FORMAT_HPP__
NEFORCE_NODISCARD constexpr size_type size() const noexcept
获取字符串长度
NEFORCE_NODISCARD constexpr basic_string_view substr(const size_type off=0, size_type count=npos) const
获取子视图
NEFORCE_NODISCARD constexpr bool empty() const noexcept
检查是否为空
NEFORCE_CONSTEXPR20 void reserve(const size_type n)
预留容量
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 size_type size() const noexcept
获取字符数
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 basic_string substr(const size_type off=0, size_type count=npos) const
获取子串
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 bool empty() const noexcept
检查是否为空
NEFORCE_NODISCARD constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
NEFORCE_INLINE17 constexpr bool is_floating_point_v
is_floating_point的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_unpackaged_v
is_unpackaged的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_signed_v
is_signed的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_unsigned_v
is_unsigned的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_standard_integral_v
is_standard_integral的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_base_of_v
is_base_of的便捷变量模板
NEFORCE_CONST_FUNCTION NEFORCE_CONSTEXPR14 bool is_digit(const CharT c) noexcept
检查字符是否为数字
unsigned char uint8_t
8位无符号整数类型
unsigned long long uint64_t
64位无符号整数类型
decltype(nullptr) nullptr_t
空指针类型
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string format(const string_view fmt, Args &&... args)
格式化字符串
format_align
对齐方式枚举
format_type
数值类型格式枚举
@ RIGHT
右对齐 '>'
@ DEFAULT
默认(数字右对齐,其他左对齐)
@ LEFT
左对齐 '<'
@ NUMERIC
符号感知填充 '='
@ CENTER
居中 '^'
@ CHAR
字符 'c'
@ DECIMAL
十进制 'd' / 'f' / 'g'
@ HEX
十六进制 'x' / 'X'
@ DEFAULT
默认(由类型决定)
@ OCTAL
八进制 'o'
@ BINARY
二进制 'b'
@ GENERAL
通用浮点 'g' / 'G'
@ SCIENTIFIC
科学计数法 'e' / 'E'
@ FIXED
固定小数 'f'
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
NEFORCE_CONSTEXPR14 int sign(const T &value) noexcept
获取数值的符号
NEFORCE_CONST_FUNCTION 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)))
移动范围元素
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_fixed(T x, int precision=6)
将浮点数转换为字符串(固定小数格式)
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_scientific(T x, int precision=6)
将浮点数转换为字符串(科学计数法格式)
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_general(T x, int precision=6)
将浮点数转换为字符串(通用格式)
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string address_string(const void *p)
将指针转换为十六进制地址字符串
basic_string< char > string
字符字符串
basic_string_view< char > string_view
字符字符串视图
typename conditional< Test, T1, T2 >::type conditional_t
conditional的便捷别名
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名
格式选项
bool space_sign
是否空格占位符号 ' '
format_align align
对齐方式
int precision
精度(-1表示默认)
bool show_sign
是否强制显示符号 '+'
bool uppercase
是否大写
char fill
填充字符
bool zero_pad
是否零填充
format_type type
类型
bool alternate
是否备用格式(# 前缀)
int width
最小宽度(0表示不限制)
NEFORCE_CONSTEXPR20 string operator()(const T &value, const format_options &options) const
格式化浮点数
NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options &options) const
格式化有符号整数
NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options &options) const
格式化无符号整数
格式化器主模板
变量处理异常
类型到字符串的转换函数