第4.2章: ::fast_io::char_category

English

概述

::fast_io::char_category 模块提供了一组 constexpr 字符分类与变换函数。 这些函数判断一个字符是小写、大写、数字、 空白、标点,等等。

所有函数都遵循程序的执行字符集(execution charset)。 这确保在基于 ASCII 和基于 EBCDIC 的系统上行为都正确。

1. 源字符集 vs 执行字符集

C++ 区分:

::fast_io::char_category 始终使用执行字符集。

这意味着:

2. ::fast_io::char_literal_v 与 char_literal

创建遵循执行字符集的字符常量,主要方式是 ::fast_io::char_literal_v。它在编译期取一个 char8_t 值,并产出执行字符集中的一个字符。


char c = ::fast_io::char_literal_v<u8'a', char>;

只要你手上有编译期的字符字面量,就用这种形式。

还有一个函数形式 ::fast_io::char_literal,它在运行时 取一个 char8_t 值:


char8_t runtime_ch{/* ... */};
char c = ::fast_io::char_literal<char>(runtime_ch);

这个版本更灵活,但可能更慢,因为它必须在运行时执行。 当字面量在编译期已知时,优先使用 char_literal_v。

3. ::fast_io::arithmetic_char_literal_v 与 arithmetic_char_literal

::fast_io::arithmetic_char_literal 的行为类似 char_literal,但它是为算术表达式设计的, 尤其是当 wchar_t 使用非 UTF 的执行字符集时。


char8_t runtime_ch{/* ... */};
char c = ::fast_io::arithmetic_char_literal<char>(runtime_ch);

简写形式 arithmetic_char_literal_v 是 编译期版本:


wchar_t w = ::fast_io::arithmetic_char_literal_v<u8'b', wchar_t>;

当你手上是编译期 char8_t 字面量,并且需要执行字符集中 适合算术运算的值时,请使用 arithmetic_char_literal_v。

4. 基本分类

每个分类函数接受单个字符,返回 true 或 false。


bool b1 = ::fast_io::char_category::is_c_lower('a');
bool b2 = ::fast_io::char_category::is_c_upper('A');
bool b3 = ::fast_io::char_category::is_c_digit('7');
bool b4 = ::fast_io::char_category::is_c_space(' ');

5. 配合 ::fast_io::string 使用 char_category

::fast_io::string 连续存储字符,因此你可以 遍历它并应用任何分类函数。


::fast_io::string s{"Hello World"};

for(char ch : s)
{
    if(::fast_io::char_category::is_c_lower(ch))
    {
        // lowercase letter
    }
}

6. 示例:统计小写字母


::fast_io::string s{"Hello fast_io!"};

std::size_t count{};

for(char ch : s)
{
    if(::fast_io::char_category::is_c_lower(ch))
    {
        ++count;
    }
}

7. 为什么不直接比较范围?

你可能会想通过与某个范围比较来检测小写字母:


if(ch >= ::fast_io::char_literal_v<u8'a', char> &&
   ch <= ::fast_io::char_literal_v<u8'z', char>)
{
    ++count;
}

即使这样使用了 char_literal_v,并让端点遵循执行 字符集,它仍然假定所有小写字母构成一个单一的连续区间。这 对 ASCII 成立,但并非对所有执行字符集(例如 EBCDIC)都保证成立。

始终优先使用:


if(::fast_io::char_category::is_c_lower(ch))
{
    ++count;
}

is_c_lower 的实现具备关于执行字符集的正确知识, 不依赖天真的区间假设。

8. 示例:筛选字母字符


::fast_io::string input{"Hello 123 World!"};
::fast_io::string letters{};

for(char ch : input)
{
    if(::fast_io::char_category::is_c_alpha(ch))
    {
        letters.push_back(ch);
    }
}

9. 变换整个字符串

::fast_io::char_category::ranges 命名空间提供了 一次作用于整个区间的函数。


::fast_io::string s{"Hello FAST_IO!"};

::fast_io::char_category::ranges::to_c_lower(s);

调用之后,s 变成 "hello fast_io!"。


::fast_io::char_category::ranges::to_c_upper(s);
::fast_io::char_category::ranges::to_c_halfwidth(s);

10. 字符分类函数

函数说明
is_c_alnum字母或数字
is_c_alpha字母
is_c_blank空格或制表符
is_c_cntrl控制字符
is_c_digit数字 0–9
is_c_fullwidth全角字符
is_c_graph可见(非空格)字符
is_c_halfwidth半角字符
is_c_lower小写字母
is_c_print可打印字符
is_c_punct标点符号
is_c_space空白
is_c_upper大写字母
is_c_xdigit十六进制数字
is_html_whitespaceHTML 空白
is_dos_file_invalid_characterDOS 文件名中的非法字符

11. 字符变换函数

函数说明
to_c_lower把单个字符转换为小写
to_c_upper把单个字符转换为大写
to_c_halfwidth把单个字符转换为半角
ranges::to_c_lower把整个区间转换为小写
ranges::to_c_upper把整个区间转换为大写
ranges::to_c_halfwidth把整个区间转换为半角

12. 使用 char_category_family 与 char_category_traits

12.1 char_category_family 定义


enum class char_category_family : ::std::uint_least32_t
{
    c_alnum,                    // Alphanumeric characters (letters and digits)
    c_alpha,                    // Alphabetic Character
    c_blank,                    // Space or tab
    c_cntrl,                    // Control characters (ASCII 0x00-0x1F, 0x7F or EBCDIC equivalents)
    c_digit,                    // Numeric digits (0-9)
    c_fullwidth,                // Full-width character
    c_graph,                    // Graphical characters (alphanumeric + punctuation)
    c_halfwidth,                // Half-width character
    c_lower,                    // Lowercase alphabetic characters
    c_print,                    // Printable characters (includes space)
    c_punct,                    // Punctuation characters
    c_space,                    // Whitespace characters (space, tab, newline, etc.)
    c_upper,                    // Uppercase alphabetic characters
    c_xdigit,                   // Hexadecimal digits (0-9, A-F, a-f)
    dos_file_invalid_character, // DOS Path invalid character
    html_whitespace             // HTML whitespace
};

12.2 创建分类器


using lower_pred =
    ::fast_io::char_category::char_category_traits<
        ::fast_io::char_category::char_category_family::c_lower,
        false
    >;

lower_pred pred{};
bool a = pred('a');   // true
bool b = pred('Z');   // false

12.3 取反的分类器


using not_lower_pred =
    ::fast_io::char_category::char_category_traits<
        ::fast_io::char_category::char_category_family::c_lower,
        true
    >;

not_lower_pred pred{};
bool a = pred('A');   // true (because 'A' is NOT lowercase)

12.4 配合 ::fast_io::string 使用 traits


::fast_io::string s{"Hello 123 World"};

using digit_pred =
    ::fast_io::char_category::char_category_traits<
        ::fast_io::char_category::char_category_family::c_digit,
        false
    >;

auto it = digit_pred::find(s.begin(), s.end());
if(it != s.end())
{
    // *it is the first digit in the string
}

这为你提供了一种灵活、通用的方式,在遵循执行字符集的前提下 对文本进行分类和搜索。

要点