Input/output library
cppreference Input/output library
有三种 io
库:
OOP
样式,基于流的io
库- 基于
print
的系列函数 - C 样式的
IO
函数
基于流的 'io' 库
基于流的 'io' 库是围绕抽象的 io
设备来进行组织的,这些抽象的设备允许使用相同的代码处理输入/输出文件,内存流,或定制的适配器设备,其动态执行任意操作(例如压缩)。
ios_base
basic_ios
basic_ostream
basic_ostringstream
basic_ofstream
basic_istream
basic_istringstream
basic_ifstream
basic_iostream
basic_stringstream
basic_fstream
ios_base
ios_base
state info
control info
它可以实现为两个任意长度的数组,或者一个由两个元素组成的数组结构体或另一个容器。
callbacks
flag
precison
width
formatting
exception mask
buffer error state
imbued locale
xalloc()
dec, oct, hex, basefield, left, right, internal, adjustfield, scientific, fixed, floatfield, boolalpha, showbase, showpoint, showpos, skipws, unitbuf, uppercase
在控制台乱码的情况
在源文件中设置 locale
能不能解决控制台的乱码情况?
#include <iostream>
#include <locale>
int main(int argc, char *argv[]) {
std::setlocale(LC_ALL, "");
std::cout << "你是你世界的主角,戏份由你安排!" << '\n';
return 0;
}
编译: g++ .\setlocale.cpp -o jkl
$ chcp 437
Active code page: 437
$ .\jkl.exe
����~_����,-�O�s,�,��'��O�^?����"���r%�Z'��?
编译: g++ .\setlocale.cpp -finput-charset=utf-8 -o jkl
$ chcp 437
Active code page: 437
$ .\jkl.exe
����~_����,-�O�s,�,��'��O�^?����"���r%�Z'��?
$ chcp 65001
Active code page: 65001
$ .\jkl.exe
ä
setlocale(LC_ALL, "en_US.UTF-8");
#include <iostream>
#include <locale>
int main(int argc, char *argv[]) {
//std::setlocale(LC_ALL, "");
std::setlocale(LC_ALL, "en_US.UTF_8");
std::cout << "你是你世界的主角,戏份由你安排!" << '\n';
return 0;
}
编译:g++ .\setlocale.cpp -o kkp
$ chcp 437
Active code page: 437
$ .\kkp.exe
你是你世界的主角,戏份由你安排!
用编译器的 utf8
选项来编译,能不能在 chcp 437
的控制台上正确的显示中文?
#include <iostream>
int main() {
std::cout << "你是最棒的!" << std::endl;
}
编译: g++ .\compile-use-utf8.cpp -finput-charset=utf-8 -o jkl
$ chcp 437
Active code page: 437
$ .\jkl.exe
你是最棒的!
fmt
库
fmt
是一个开源的,格式化库,它是快速、安全的,可以替代 C stdio
和 C++ iostreams
。
特征:
- 简单的 api
C++20 std::format
和C++23 std::print
的实现- 类似于
python
格式化语法 - IEEE 754 的快述圆整,使用Dragonbox算法保证短途和往返
- 可移植的
unicode
支持 - 安全的
printf 实现
,包含POSIX
对位置参数的扩展 - 可扩展:支持用户自定义类型
- 高性能:比
(s)printf
,iostream
,to_string
和to_chars
快 - 在源代码方面代码量小,最小配置仅由三个文件组成:
core.h
,format.h
和format-inl.h
- 可靠性:该库有一套广泛的测试,并且不断被模糊化
- 安全性:该库完全类型安全,格式字符串中的错误可以在编译时报告,自动内存管理可防止缓冲区溢出错误
- 易用性:小型自包含代码库,无外部依赖,宽松的MIT许可证
- 可移植性,跨平台输出一致,支持旧编译器
- 即使在高警告级别(如-Wall-Wextra-pedantic)上,也要清理无警告的代码库
- 默认情况下独立于区域设置
- 使用FMT_header_only宏启用可选的仅标头配置
使用第一个参数打印,使用 s
的指定类型为 string
(string
的打印需要手动指定类型,而 int
等不需要) ,中间对齐 ^
,两侧填充 -
,宽度为 30
:
(具体的格式化标记见:https://fmt.dev/11.0/)
#define FMT_HEADER_ONLY 1
#include <fmt/format.h>
#include <string>
int main() {
fmt::print("{0:-^30s}\n", std::string("center"));
}
fmt
的组件
fmt/base.h
:基本的 API 提供主要的格式化函数fmt/format.h
:fmt::format
和其他的格式化函数,以及 locale 支持fmt/ranges.h
: 对ranges
和tuple
进行格式化fmt/chrono.h
: 日期和时间的格式化fmt/std.h
:用于标准库类型的格式器fmt/compile.h
:格式字符串编译fmt/color.h
:终端的色彩和文本样式fmt/os.h
:系统 APIsfmt/ostream.h
:std::ostream
支持fmt/args.h
:动态参数列表fmt/printf.h
:printf
格式化fmt/xchar.h
:可选的wchar_t
支持
fmt
库中提供的类型和函数都在命名空间 fmt
中,然后库的所有宏都有前缀 FMT_
。
在 cmake
中使用
FetchContent: 从 cmake 3.11 开始,使用
FetchContent自动下载
fmt` 作为配置时的依赖项:
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281) # 10.2.1
FetchContent_MakeAvailable(fmt)
target_link_libraries(<your-target> fmt::fmt)
已安装:找到已安装 fmt ,然后在你的 CMakeLists.txt
中使用:
find_package(fmt)
target_link_libraries(<your-target> fmt::fmt)
直接嵌入: 直接使用源码树
在你的项目中,在你的 CMakeLists.txt
中使用:
add_subdirectory(fmt)
target_link_libraries(<your-target> fmt::fmt)
Header-only
的方式使用
https://github.com/fmtlib/fmt/issues/524
You don't need CMake for that, just define FMT_HEADER_ONLY macro to 1 when including fmt/format.h.
replacement-field
替换字段的使用和它的格式化
replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
arg_id ::= integer | identifier
integer ::= digit+
digit ::= "0"..."9"
identifier ::= id_start id_continue*
id_start ::= "a"..."z" | "A"..."Z" | "_"
id_continue ::= id_start | digit
format_spec
可以是,字段宽,对齐,填充,数字精度等。format_spec
可以包含嵌套的替换字段在其中确切的位置。
format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
fill ::= <a character other than '{' or '}'>
align ::= "<" | ">" | "^"
sign ::= "+" | "-" | " "
width ::= integer | "{" [arg_id] "}"
precision ::= integer | "{" [arg_id] "}"
type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" |
"g" | "G" | "o" | "p" | "s" | "x" | "X" | "?"
chrono_format_spec
chrono_format_spec ::= [[fill]align][width]["." precision][chrono_specs]
chrono_specs ::= conversion_spec |
chrono_specs (conversion_spec | literal_char)
conversion_spec ::= "%" [padding_modifier] [locale_modifier] chrono_type
literal_char ::= <a character other than '{', '}' or '%'>
padding_modifier ::= "-" | "_" | "0"
locale_modifier ::= "E" | "O"
chrono_type ::= "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" |
"F" | "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" |
"n" | "p" | "q" | "Q" | "r" | "R" | "S" | "t" | "T" |
"u" | "U" | "V" | "w" | "W" | "x" | "X" | "y" | "Y" |
"z" | "Z" | "%"
Chrono Format Specifications
chrono_format_spec ::= [[fill]align][width]["." precision][chrono_specs]
chrono_specs ::= conversion_spec |
chrono_specs (conversion_spec | literal_char)
conversion_spec ::= "%" [padding_modifier] [locale_modifier] chrono_type
literal_char ::= <a character other than '{', '}' or '%'>
padding_modifier ::= "-" | "_" | "0"
locale_modifier ::= "E" | "O"
chrono_type ::= "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" |
"F" | "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" |
"n" | "p" | "q" | "Q" | "r" | "R" | "S" | "t" | "T" |
"u" | "U" | "V" | "w" | "W" | "x" | "X" | "y" | "Y" |
"z" | "Z" | "%"
Range Format Specifications
范围类型的格式规范具有以下语法:
range_format_spec ::= ["n"][range_type][range_underlying_spec]
fmt::print("{}", std::vector{10, 20, 30});
// Output: [10, 20, 30]
fmt::print("{::#x}", std::vector{10, 20, 30});
// Output: [0xa, 0x14, 0x1e]
fmt::print("{}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: ['h', 'e', 'l', 'l', 'o']
fmt::print("{:n}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: 'h', 'e', 'l', 'l', 'o'
fmt::print("{:s}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: "hello"
fmt::print("{:?s}", std::vector{'h', 'e', 'l', 'l', 'o', '\n'});
// Output: "hello\n"
fmt::print("{::}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: [h, e, l, l, o]
fmt::print("{::d}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: [104, 101, 108, 108, 111]
template <typename... T>
void print(FILE* f, format_string<T...> fmt, T&&... args);
FILE
stream state
除了系统指定的,访问设备所必需的信息外(也就是 POSIX 文件描述符),每 FILE
对象都直接或间接的持有以下的信息:
- 字符宽度:unset, narror, or wide.
- 用于在多字节和宽字符间转换时(
mbstate_t
对象的类型),的解析状态。 - 缓冲状态:unbuffered, line-buffered, fully buffered。
- 一个缓冲,可能被外部的、用户提供的缓冲所替代。
I/O
模式: input, output 和 update(input 和 output)。- Binary/text 模式说明符。
- End-of-file 状态说明符。
- 文件位置说明符,一个可访问的对象,有着
fpos_t
类型,对于wide streams
,包含解析状。 - 可重入的锁,用于在多线程读、写、定位或查询流的位置时,防止数据竞争。
Narrow 和 wide 的面向
新打开的流没有方向。对fwide或任何I/O函数的第一次调用建立了定向:<详情见原文>
窄向,宽向
二进制模式和文本模式
文本流是一个有序的字符序列,能组合进一行中;'\n'
文本流中表示一行。最后一行是否需要由 '\n'
终结是实现定义的。此外,可能需要在输入和输出时添加、修改或删除字符,以符合操作系统中表示文本的约定(特别是,Windows操作系统上的C流在输出时将'\n'转换为'\r\n',并在输入时将'\r\n'转换为'\n')。
读取的数据可以等同于写入的数据,只要以下的条件为真:
- 数据的构成由唯一的可打印的字和/或唯一的控制字符
'\t'
和'\n'
(实践中,在 windows 系统,`'\0x1A' 会终结输入)。 - 没有空格字符紧接在'\n'字符前面(这些空格字符可能在以后将输出作为输入读取时消失)。
- 最后的字符是
'\n'
。
二进制流是一个有序的字符序列,可以透明地记录内部数据。从二进制流读入的数据总是等于先前写入该流的数据,除了允许在流的末尾附加不确定数量的空字符。宽二进制流不需要在初始移位状态中结束。
stdin, stdout, stderr
stdin expression of type FILE*
associated with the input stream
stdout expression of type FILE*
associated with the output stream
stderr expression of type FILE*
associated with the error output stream
unsigned int fread(void *buffer, unsigned int size, unsigned int count, FILE *fp);
unsigned int fwrite(const void* buffer, unsigned int size, unsigned int count, FILE *fp);
将 buffer
指向的内存中的数据块写入 fp
所指的文件,buffer
k待输出数据块的起始地址,size
是每个数据块的大小(字节数),count
是最多允许写入的数据块的个数(每个数据块是 size
个字节),函数返回的是实际写入的数据块的个数。
fread()函数和 fwrite()函数是按数据块的长度来处理输入/输出的,在用文本编辑器打开文本文件时可能因发生字符转换而出现莫名其妙的结果,所以这两个函数通常用于二进制文件的输入/输出