Console Library
8.0.0
A header-only library that makes C++ simple
Toggle main menu visibility
Loading...
Searching...
No Matches
fmt.h
Go to the documentation of this file.
1
9
10
/*
11
Copyright (c) 2026 MrXie1109
12
13
Permission is hereby granted, free of charge, to any person obtaining a copy
14
of this software and associated documentation files (the "Software"), to deal
15
in the Software without restriction, including without limitation the rights
16
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
copies of the Software, and to permit persons to whom the Software is
18
furnished to do so, subject to the following conditions:
19
20
The above copyright notice and this permission notice shall be included in all
21
copies or substantial portions of the Software.
22
23
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
SOFTWARE.
30
*/
31
32
#pragma once
33
#include <cctype>
34
#include <iomanip>
35
#include <iostream>
36
#include <sstream>
37
#include <stdexcept>
38
#include <string>
39
40
#include "
../core/csexc.h
"
41
#include "
../io/output.h
"
42
43
namespace
console
{
44
namespace
details
{
49
template
<
class
CharT =
char
>
50
struct
BasicFormatSpec
{
55
CharT
fill
=
static_cast<
CharT
>
(
' '
);
60
CharT
align
=
static_cast<
CharT
>
(
'>'
);
65
int
width
= 0;
70
int
precision
= -1;
77
CharT
type
= 0;
82
bool
hasHash
=
false
;
87
bool
hasPlus
=
false
;
92
bool
hasSpace
=
false
;
93
};
94
102
template
<
class
CharT =
char
>
103
void
parse_format_spec
(
const
std::basic_string<CharT> &specStr,
104
BasicFormatSpec<CharT>
&spec) {
105
size_t
i = 0;
106
size_t
len = specStr.size();
107
if
(len == 0)
return
;
108
109
if
(i + 1 < len) {
110
CharT first = specStr[i];
111
CharT second = specStr[i + 1];
112
if
(first ==
static_cast<
CharT
>
(
'0'
)
113
&& std::isdigit(
static_cast<
unsigned
char
>
(second))) {
114
spec.
fill
= first;
115
spec.
align
=
static_cast<
CharT
>
(
'>'
);
116
i += 1;
117
}
else
if
(second ==
static_cast<
CharT
>
(
'<'
)
118
|| second ==
static_cast<
CharT
>
(
'>'
)) {
119
if
(first ==
static_cast<
CharT
>
(
'{'
)
120
|| first ==
static_cast<
CharT
>
(
'}'
)) {
121
throw
BadFormat
(
"invalid fill character"
);
122
}
123
spec.
fill
= first;
124
spec.
align
= second;
125
i += 2;
126
}
else
if
(first ==
static_cast<
CharT
>
(
'<'
)
127
|| first ==
static_cast<
CharT
>
(
'>'
)) {
128
spec.
align
= first;
129
i += 1;
130
}
131
}
132
133
if
(i < len
134
&& std::isdigit(
static_cast<
unsigned
char
>
(specStr[i]))) {
135
size_t
start = i;
136
while
(i < len
137
&& std::isdigit(
static_cast<
unsigned
char
>
(specStr[i])))
138
i++;
139
spec.
width
= std::stoi(
140
std::string(specStr.begin() + start, specStr.begin() + i));
141
}
142
143
if
(i < len && specStr[i] ==
static_cast<
CharT
>
(
':'
)) {
144
i++;
145
while
(i < len
146
&& (specStr[i] ==
static_cast<
CharT
>
(
'#'
)
147
|| specStr[i] ==
static_cast<
CharT
>
(
'+'
)
148
|| specStr[i] ==
static_cast<
CharT
>
(
' '
))) {
149
if
(specStr[i] ==
static_cast<
CharT
>
(
'#'
))
150
spec.
hasHash
=
true
;
151
else
if
(specStr[i] ==
static_cast<
CharT
>
(
'+'
))
152
spec.
hasPlus
=
true
;
153
else
if
(specStr[i] ==
static_cast<
CharT
>
(
' '
))
154
spec.
hasSpace
=
true
;
155
i++;
156
}
157
if
(i < len && specStr[i] ==
static_cast<
CharT
>
(
'.'
)) {
158
i++;
159
size_t
start = i;
160
while
(
161
i < len
162
&& std::isdigit(
static_cast<
unsigned
char
>
(specStr[i])))
163
i++;
164
if
(start == i)
throw
BadFormat
(
"missing precision"
);
165
spec.
precision
= std::stoi(std::string(
166
specStr.begin() + start, specStr.begin() + i));
167
}
168
if
(i < len
169
&& std::isalpha(
static_cast<
unsigned
char
>
(specStr[i]))) {
170
spec.
type
= specStr[i];
171
i++;
172
}
173
if
(i < len) {
174
throw
BadFormat
(
175
"unexpected characters at end: "
176
+ std::string(specStr.begin() + i, specStr.end()));
177
}
178
}
else
if
(i < len) {
179
std::basic_string<CharT> remaining = specStr.substr(i);
180
if
(remaining[0] ==
static_cast<
CharT
>
(
'.'
)) {
181
i++;
182
size_t
start = i;
183
while
(
184
i < len
185
&& std::isdigit(
static_cast<
unsigned
char
>
(specStr[i])))
186
i++;
187
if
(start == i)
throw
BadFormat
(
"missing precision"
);
188
spec.
precision
= std::stoi(std::string(
189
specStr.begin() + start, specStr.begin() + i));
190
if
(i < len
191
&& std::isalpha(
192
static_cast<
unsigned
char
>
(specStr[i]))) {
193
spec.
type
= specStr[i];
194
i++;
195
}
196
if
(i < len) {
197
throw
BadFormat
(
198
"unexpected characters after precision: "
199
+ std::string(specStr.begin() + i, specStr.end()));
200
}
201
}
else
{
202
// 标志 + [.精度] + [类型]
203
size_t
j = 0;
204
while
(j < remaining.size()
205
&& (remaining[j] ==
static_cast<
CharT
>
(
'#'
)
206
|| remaining[j] ==
static_cast<
CharT
>
(
'+'
)
207
|| remaining[j] ==
static_cast<
CharT
>
(
' '
))) {
208
if
(remaining[j] ==
static_cast<
CharT
>
(
'#'
))
209
spec.
hasHash
=
true
;
210
else
if
(remaining[j] ==
static_cast<
CharT
>
(
'+'
))
211
spec.
hasPlus
=
true
;
212
else
if
(remaining[j] ==
static_cast<
CharT
>
(
' '
))
213
spec.
hasSpace
=
true
;
214
j++;
215
}
216
if
(j < remaining.size()
217
&& remaining[j] ==
static_cast<
CharT
>
(
'.'
)) {
218
j++;
219
size_t
prec_start = j;
220
while
(j < remaining.size()
221
&& std::isdigit(
222
static_cast<
unsigned
char
>
(remaining[j])))
223
j++;
224
if
(prec_start == j)
225
throw
BadFormat
(
"missing precision"
);
226
spec.
precision
= std::stoi(
227
std::string(remaining.begin() + prec_start,
228
remaining.begin() + j));
229
}
230
if
(j < remaining.size()) {
231
if
(remaining.size() - j == 1
232
&& std::isalpha(
233
static_cast<
unsigned
char
>
(remaining[j]))) {
234
spec.
type
= remaining[j];
235
j++;
236
}
else
{
237
throw
BadFormat
(
"unexpected character '"
238
+ std::string(1,
239
static_cast<
char
>
(remaining[j]))
240
+
"'"
);
241
}
242
}
243
if
(j < remaining.size()) {
244
throw
BadFormat
(
"unexpected characters: "
245
+ std::string(remaining.begin() + j,
246
remaining.end()));
247
}
248
}
249
}
250
251
if
(spec.
hasHash
) {
252
char
t =
static_cast<
char
>
(spec.
type
);
253
if
(t != 0 && t !=
'd'
&& t !=
'x'
&& t !=
'X'
&& t !=
'o'
254
&& t !=
'f'
&& t !=
'F'
&& t !=
'g'
&& t !=
'G'
) {
255
throw
BadFormat
(
"'#' not allowed with type '"
256
+ std::string(1, t) +
"'"
);
257
}
258
}
259
if
(spec.
hasPlus
&& spec.
hasSpace
) {
260
throw
BadFormat
(
"'+' and space cannot both appear"
);
261
}
262
if
(spec.
precision
>= 0) {
263
char
t =
static_cast<
char
>
(spec.
type
);
264
if
(t != 0 && t !=
'f'
&& t !=
'F'
&& t !=
'e'
&& t !=
'E'
265
&& t !=
'g'
&& t !=
'G'
&& t !=
's'
) {
266
throw
BadFormat
(
"precision not allowed with type '"
267
+ std::string(1, t) +
"'"
);
268
}
269
}
270
}
271
278
template
<
class
CharT =
char
>
279
void
apply_format
(
280
std::basic_ostream<CharT> &os,
const
BasicFormatSpec<CharT>
&spec) {
281
if
(spec.
width
> 0) os << std::setw(spec.
width
);
282
if
(spec.
fill
!=
static_cast<
CharT
>
(
' '
))
283
os << std::setfill(spec.
fill
);
284
if
(spec.
align
==
static_cast<
CharT
>
(
'<'
))
285
os << std::left;
286
else
if
(spec.
align
==
static_cast<
CharT
>
(
'>'
))
287
os << std::right;
288
if
(spec.
precision
>= 0) os << std::setprecision(spec.
precision
);
289
if
(spec.
type
!= 0) {
290
switch
(
static_cast<
char
>
(spec.
type
)) {
291
case
'd'
:
292
os << std::dec;
293
break
;
294
case
'x'
:
295
os << std::hex << std::nouppercase;
296
break
;
297
case
'X'
:
298
os << std::hex << std::uppercase;
299
break
;
300
case
'o'
:
301
os << std::oct;
302
break
;
303
case
'f'
:
304
case
'F'
:
305
os << std::fixed;
306
break
;
307
case
'e'
:
308
os << std::scientific;
309
break
;
310
case
'E'
:
311
os << std::scientific << std::uppercase;
312
break
;
313
case
'g'
:
314
os << std::defaultfloat;
315
break
;
316
case
'G'
:
317
os << std::defaultfloat << std::uppercase;
318
break
;
319
default
:
320
break
;
321
}
322
}
323
if
(spec.
hasHash
) os << std::showbase;
324
if
(spec.
hasPlus
) os << std::showpos;
325
}
326
327
// 辅助:算术类型正数前加空格
328
template
<
class
CharT,
class
T>
329
static
typename
std::enable_if<std::is_arithmetic<T>::value,
void
>::type
330
apply_space_if_nonneg
(std::basic_ostream<CharT> &os,
const
T &value) {
331
if
(value >= 0) os << static_cast<CharT>(
' '
);
332
}
333
334
template
<
class
CharT,
class
T>
335
static
336
typename
std::enable_if<!std::is_arithmetic<T>::value,
void
>::type
337
apply_space_if_nonneg
(std::basic_ostream<CharT> &,
const
T &) {}
338
347
template
<
typename
CharT =
char
,
typename
T>
348
void
format_value
(std::basic_ostream<CharT> &os,
349
const
T &value,
350
const
BasicFormatSpec<CharT>
&spec) {
351
std::ios_base::fmtflags oldFlags = os.flags();
352
CharT oldFill = os.fill();
353
std::streamsize oldPrecision = os.precision();
354
apply_format
(os, spec);
355
if
(spec.
hasSpace
)
apply_space_if_nonneg
(os, value);
356
put
(os, value);
357
os.flags(oldFlags);
358
os.fill(oldFill);
359
os.precision(oldPrecision);
360
}
361
370
template
<
class
CharT =
char
>
371
void
format_impl
(std::basic_ostream<CharT> &os,
372
const
std::basic_string<CharT> &fmt,
373
size_t
&pos,
374
size_t
) {
375
while
(pos < fmt.size()) {
376
CharT ch = fmt[pos];
377
if
(ch ==
static_cast<
CharT
>
(
'{'
) && pos + 1 < fmt.size()
378
&& fmt[pos + 1] ==
static_cast<
CharT
>
(
'{'
)) {
379
os << static_cast<CharT>(
'{'
);
380
pos += 2;
381
continue
;
382
}
383
if
(ch ==
static_cast<
CharT
>
(
'}'
) && pos + 1 < fmt.size()
384
&& fmt[pos + 1] ==
static_cast<
CharT
>
(
'}'
)) {
385
os << static_cast<CharT>(
'}'
);
386
pos += 2;
387
continue
;
388
}
389
if
(ch ==
static_cast<
CharT
>
(
'{'
)) {
390
size_t
start = pos;
391
pos++;
392
size_t
closePos = fmt.find(
static_cast<
CharT
>
(
'}'
), pos);
393
if
(closePos == std::basic_string<CharT>::npos) {
394
throw
BadFormat
(
"Unmatched '{' at position "
395
+ std::to_string(start));
396
}
397
throw
BadFormat
(
398
"Not enough arguments for placeholder at position "
399
+ std::to_string(start));
400
}
else
if
(ch ==
static_cast<
CharT
>
(
'}'
)) {
401
size_t
start = pos;
402
pos++;
403
throw
BadFormat
(
404
"Unmatched '}' at position "
+ std::to_string(start));
405
}
else
{
406
os << ch;
407
pos++;
408
}
409
}
410
}
411
424
template
<
class
CharT = char,
typename
T
,
typename
... Args>
425
void
format_impl
(std::basic_ostream<CharT> &os,
426
const
std::basic_string<CharT> &fmt,
427
size_t
&pos,
428
size_t
argIndex,
429
const
T &first,
430
const
Args &...rest) {
431
while
(pos < fmt.size()) {
432
CharT ch = fmt[pos];
433
if
(ch ==
static_cast<
CharT
>
(
'{'
) && pos + 1 < fmt.size()
434
&& fmt[pos + 1] ==
static_cast<
CharT
>
(
'{'
)) {
435
os << static_cast<CharT>(
'{'
);
436
pos += 2;
437
continue
;
438
}
439
if
(ch ==
static_cast<
CharT
>
(
'}'
) && pos + 1 < fmt.size()
440
&& fmt[pos + 1] ==
static_cast<
CharT
>
(
'}'
)) {
441
os << static_cast<CharT>(
'}'
);
442
pos += 2;
443
continue
;
444
}
445
if
(ch ==
static_cast<
CharT
>
(
'{'
)) {
446
size_t
start = pos;
447
pos++;
448
size_t
closePos = fmt.find(
static_cast<
CharT
>
(
'}'
), pos);
449
if
(closePos == std::basic_string<CharT>::npos) {
450
throw
BadFormat
(
"Unmatched '{' at position "
451
+ std::to_string(start));
452
}
453
std::basic_string<CharT> placeholder
454
= fmt.substr(pos, closePos - pos);
455
if
(!placeholder.empty()
456
&& placeholder[0] ==
static_cast<
CharT
>
(
':'
)) {
457
placeholder = placeholder.substr(1);
458
}
459
pos = closePos + 1;
460
BasicFormatSpec<CharT>
spec;
461
try
{
462
parse_format_spec
(placeholder, spec);
463
}
catch
(
const
BadFormat
&e) {
464
throw
BadFormat
(
"Invalid format specifier '"
465
+ std::string(placeholder.begin(),
466
placeholder.end())
467
+
"' at position "
468
+ std::to_string(start + 1) +
": "
469
+ e.what());
470
}
471
format_value
(os, first, spec);
472
format_impl
(os, fmt, pos, argIndex + 1, rest...);
473
return
;
474
}
else
{
475
os << ch;
476
pos++;
477
}
478
}
479
}
480
}
481
482
using
FormatSpec
=
details::BasicFormatSpec<char>
;
483
using
WFormatSpec
=
details::BasicFormatSpec<wchar_t>
;
484
493
template
<
typename
... Args>
494
std::string
format
(
const
std::string &fmt,
const
Args &...args) {
495
std::ostringstream oss;
496
size_t
pos = 0;
497
try
{
498
details::format_impl
(oss, fmt, pos, 0, args...);
499
}
catch
(
const
BadFormat
&) {
500
throw
;
501
}
catch
(
const
std::exception &e) {
502
throw
BadFormat
(std::string(
"Internal error: "
) + e.what());
503
}
504
return
oss.str();
505
}
506
515
template
<
typename
... Args>
516
std::wstring
format
(
const
std::wstring &fmt,
const
Args &...args) {
517
std::wostringstream oss;
518
size_t
pos = 0;
519
try
{
520
details::format_impl
(oss, fmt, pos, 0, args...);
521
}
catch
(
const
BadFormat
&) {
522
throw
;
523
}
catch
(
const
std::exception &e) {
524
throw
BadFormat
(std::string(
"Internal error: "
) + e.what());
525
}
526
return
oss.str();
527
}
528
537
template
<
class
CharT,
class
Traits>
538
template
<
typename
... Args>
539
BasicOutput<CharT, Traits>
&
BasicOutput<CharT, Traits>::fmt
(
540
const
std::basic_string<CharT, Traits> &format_str,
541
const
Args &...args) {
542
size_t
pos = 0;
543
details::format_impl
(
os
, format_str, pos, 0, args...);
544
os
<<
end
;
545
if
(
isFlush
)
os
<< std::flush;
546
return
*
this
;
547
}
548
557
template
<
class
CharT,
class
Traits>
558
template
<
typename
... Args>
559
BasicOutput<CharT, Traits>
&
BasicOutput<CharT, Traits>::fmt
(
560
const
CharT *format_str,
const
Args &...args) {
561
return
fmt
(std::basic_string<CharT, Traits>(format_str), args...);
562
}
563
}
console::BadFormat
表示格式化字符串错误,例如占位符与参数不匹配。
Definition
csexc.h:68
console::BadFormat::BadFormat
BadFormat(const std::string &msg)
构造 BadFormat。
Definition
csexc.h:74
console::BasicOutput::fmt
BasicOutput & fmt(const std::basic_string< CharT, Traits > &format_str, const Args &...args)
console::BasicOutput< char >::end
string_type end
Definition
output.h:425
console::BasicOutput< char >::BasicOutput
BasicOutput()
Definition
output.h:432
console::BasicOutput< char >::isFlush
bool isFlush
Definition
output.h:426
console::BasicOutput< char >::os
std::basic_ostream< char, std::char_traits< char > > & os
Definition
output.h:423
csexc.h
定义 console 库使用的自定义异常类层次结构。
console::details
Definition
fmt.h:44
console::details::apply_space_if_nonneg
static std::enable_if< std::is_arithmetic< T >::value, void >::type apply_space_if_nonneg(std::basic_ostream< CharT > &os, const T &value)
Definition
fmt.h:330
console::details::format_value
void format_value(std::basic_ostream< CharT > &os, const T &value, const BasicFormatSpec< CharT > &spec)
格式化单个值。
Definition
fmt.h:348
console::details::format_impl
void format_impl(std::basic_ostream< CharT > &os, const std::basic_string< CharT > &fmt, size_t &pos, size_t)
格式化实现(无参数版本,递归终止)。
Definition
fmt.h:371
console::details::parse_format_spec
void parse_format_spec(const std::basic_string< CharT > &specStr, BasicFormatSpec< CharT > &spec)
解析格式说明符。
Definition
fmt.h:103
console::details::apply_format
void apply_format(std::basic_ostream< CharT > &os, const BasicFormatSpec< CharT > &spec)
将格式说明符应用到输出流。
Definition
fmt.h:279
console
本库所有组件所在的顶层命名空间。
console::Key::T
@ T
Definition
kb.h:99
console::WFormatSpec
details::BasicFormatSpec< wchar_t > WFormatSpec
Definition
fmt.h:483
console::format
std::string format(const std::string &fmt, const Args &...args)
格式化字符串(char 版)。
Definition
fmt.h:494
console::FormatSpec
details::BasicFormatSpec< char > FormatSpec
Definition
fmt.h:482
console::put
std::enable_if< is_string< typenamestd::decay< StrOrCharType >::type >::value||is_char< typenamestd::decay< StrOrCharType >::type >::value >::type put(std::basic_ostream< CharT, Traits > &os, StrOrCharType &&value)
Definition
output.h:56
output.h
提供 STL 容器的格式化输出和灵活的输出控制工具。
console::details::BasicFormatSpec
格式说明符结构。
Definition
fmt.h:50
console::details::BasicFormatSpec< char >::align
char align
Definition
fmt.h:60
console::details::BasicFormatSpec< char >::hasHash
bool hasHash
Definition
fmt.h:82
console::details::BasicFormatSpec< char >::hasPlus
bool hasPlus
Definition
fmt.h:87
console::details::BasicFormatSpec< char >::type
char type
Definition
fmt.h:77
console::details::BasicFormatSpec< char >::fill
char fill
Definition
fmt.h:55
console::details::BasicFormatSpec< char >::hasSpace
bool hasSpace
Definition
fmt.h:92
console::details::BasicFormatSpec< char >::width
int width
Definition
fmt.h:65
console::details::BasicFormatSpec< char >::precision
int precision
Definition
fmt.h:70
include
text
fmt.h
Generated on
for Console Library by
1.18.0