【问题标题】:How can I check if char encoding is ASCII?如何检查 char 编码是否为 ASCII?
【发布时间】:2020-09-20 15:28:22
【问题描述】:

我想写如下函数:

int char_index(char c) 
{
  if (is_ascii<char>)
    return c - 'A';
  else 
    return c == 'A' ? 0 :
           c == 'B' ? 1 :
           // ...
}

std中有is_ascii这样的函数吗?我在想象类似 std::numeric_limits&lt;T&gt;::is_iec559 的东西,它表示某些浮点类型 T 是否满足 IEE 754 标准的要求。


我想我可以自己实现is_ascii 类似if (65 == 'A' &amp;&amp; ...) 枚举整个ASCII 字符集,并将它们与int 表示进行比较,但这很烦人。另外,我不确定如何检查不可打印的字符,例如SOH(标题开头)等。

甚至可以在用户代码中编写这个函数,还是我必须依赖实现来提供这样一个函数?

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 我只需要使用兼容 ASCII 或 UTF-8 的编译器来编译程序。但请注意,您的函数不是 UTF-8 安全的:它不会处理像 ä 这样的多字节字符。
  • 如果您已经拥有return c == 'A' ? 0 : c == 'B' ? 1 : 等,那么您就不需要is_ascii,无论哪种情况都可以。
  • @n.'pronouns'm。是的,当然,我不想写那个。我可以断言在这种情况下不处理非 ascii 编码。 (这个例子只是展示了一种处理方式)。
  • 在文档中写下非 ascii 编码不被处理并用它来完成。

标签: c++ encoding ascii


【解决方案1】:

我假设您在将源代码中的字符串文字和字符文字转换为机器代码时要检查您的编译器是否使用 ascii 编码。

std中有is_ascii这样的函数吗?

我不知道。

我可以用 if (65 == 'A' && ...) 之类的方法自己实现 is_ascii,它会枚举整个 ASCII 字符集

那就这样做吧。检查可以是c-char的字符,所以都来自basic source character set

a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '

和转义序列:

\a  \b  \f  \n  \r  \t  \v

没有办法检查“整个”ASCII 字符集,因为编译器不会将程序转录为整个 ASCII 字符集。它只将基本字符集字符和转义序列映射到它的机器表示,而不是整个字符集(可能有编译器扩展)。

但这很烦人。

但这是唯一的方法。要验证您的实现使用了一些字符集,您必须检查它使用的所有字符。所以检查他们。无论如何都会是consteval

如何检查不可打印的字符,如 SOH(标题开头)等。

不要。 SOH 字符不能在字符文字中,您不必检查它们,因为不可能用 C 语言表达它。没有\SOH 转义序列,0x01 字节不在基本字符集内。您的编译器从不将字符序列转换为 SOH 字符。一个有效的程序只能由基本源字符集中的字符组成。 SOH 字符的解释取决于将要接收它的东西,如果我写'\001',它将是等于1 的字节,与编码无关。 p>


嗯,让我们写吧!以下程序:

#include <type_traits>
#include <algorithm>
constexpr bool compiler_uses_ascii() {
    return 
        '\a'==0x07  &&  '\b'==0x08  &&  '\t'==0x09  &&  '\n'==0x0a  &&  '\v'==0x0b  &&  '\f'==0x0c  &&
        '\r'==0x0d  &&  '!'==0x21   &&  '#'==0x23   &&  '%'==0x25   &&  '&'==0x26   &&  '\''==0x27  &&
        '('==0x28   &&  ')'==0x29   &&  '*'==0x2a   &&  '+'==0x2b   &&  ','==0x2c   &&  '-'==0x2d   &&
        '.'==0x2e   &&  '/'==0x2f   &&  '0'==0x30   &&  '1'==0x31   &&  '2'==0x32   &&  '3'==0x33   &&
        '4'==0x34   &&  '5'==0x35   &&  '6'==0x36   &&  '7'==0x37   &&  '8'==0x38   &&  '9'==0x39   &&
        ':'==0x3a   &&  ';'==0x3b   &&  '<'==0x3c   &&  '='==0x3d   &&  '>'==0x3e   &&  '?'==0x3f   &&
        'A'==0x41   &&  'B'==0x42   &&  'C'==0x43   &&  'D'==0x44   &&  'E'==0x45   &&  'F'==0x46   &&
        'G'==0x47   &&  'H'==0x48   &&  'I'==0x49   &&  'J'==0x4a   &&  'K'==0x4b   &&  'L'==0x4c   &&
        'M'==0x4d   &&  'N'==0x4e   &&  'O'==0x4f   &&  'P'==0x50   &&  'Q'==0x51   &&  'R'==0x52   &&
        'S'==0x53   &&  'T'==0x54   &&  'U'==0x55   &&  'V'==0x56   &&  'W'==0x57   &&  'X'==0x58   &&
        'Y'==0x59   &&  'Z'==0x5a   &&  '['==0x5b   &&  '\\'==0x5c  &&  ']'==0x5d   &&  '^'==0x5e   &&
        '_'==0x5f   &&  'a'==0x61   &&  'b'==0x62   &&  'c'==0x63   &&  'd'==0x64   &&  'e'==0x65   &&
        'f'==0x66   &&  'g'==0x67   &&  'h'==0x68   &&  'i'==0x69   &&  'j'==0x6a   &&  'k'==0x6b   &&
        'l'==0x6c   &&  'm'==0x6d   &&  'n'==0x6e   &&  'o'==0x6f   &&  'p'==0x70   &&  'q'==0x71   &&
        'r'==0x72   &&  's'==0x73   &&  't'==0x74   &&  'u'==0x75   &&  'v'==0x76   &&  'w'==0x77   &&
        'x'==0x78   &&  'y'==0x79   &&  'z'==0x7a   &&  '{'==0x7b   &&  '|'==0x7c   &&  '}'==0x7d   &&
        '~'==0x7e;
}
constexpr int char_index(char c)
{
    if constexpr (compiler_uses_ascii()) {
        return c - 'A';
    } else {
        // Is that right? Maybe it is.
        const char a[] = "ABCDEFGHIJKLMNOPRSTUVXYZ";
        return std::find(a, a + sizeof(a), c) - a;
#if 0
        return
            c == 'A' ? 0 :  c == 'B' ? 1 :  c == 'C' ? 2 :  c == 'D' ? 3 :
            c == 'E' ? 4 :  c == 'F' ? 5 :  c == 'G' ? 6 :  c == 'H' ? 7 :
            c == 'I' ? 8 :  c == 'J' ? 9 :  c == 'K' ? 10 : c == 'L' ? 11 :
            c == 'M' ? 12 : c == 'N' ? 13 : c == 'O' ? 14 : c == 'P' ? 15 :
            c == 'Q' ? 16 : c == 'R' ? 17 : c == 'S' ? 18 : c == 'T' ? 19 :
            c == 'U' ? 20 : c == 'V' ? 21 : c == 'W' ? 22 : c == 'X' ? 23 :
            c == 'Y' ? 24 : c == 'Z' ? 25 : -1;
#endif
    }
}
#include <iostream>
int main() {
    std::cout << compiler_uses_ascii() << " " << char_index('B') << "\n";
}

执行时输出:

$ g++ 1.cpp -std=c++20 && ./a.out
1 1
$ g++ 1.cpp -fexec-charset=IBM-1047 -std=c++20 && ./a.out
0@1%

【讨论】:

  • 所以如果我明白你在说什么,在用户代码中不可能检查整个字符集?
  • 不可能检查整个字符集,因为并非所有字符都被使用。您只能检查使用的字符。 “编码”通常是一个字节到另一个字节之间的映射。您想检查是否使用了特定的编码 - 好吧,您只能检查映射中的字符,无法检查其他字节,因为......它们不在映射中。
【解决方案2】:

您通常可以从 std::locale("").name() 获取编码信息,尽管它几乎永远不会是 ASCII,而是它的一些 超集,例如 UTF-8 或 CP1252(除非基本编码是一些非 ASCII 编码,如 EBCDIC)。现在没人用纯ASCII了

如果允许提升,那么boost::locale::info::encoding() 会为您提供更可靠的编码信息。您仍然需要检查编码以查看是否涵盖了 ASCII 集

要查看编码是否是 ASCII 的超集,您可以检查 the list here

【讨论】:

  • No one uses pure ASCII nowadays嗯,嗯..随着嵌入式和物联网设备的不断兴起,那些倾向于在-nano版本中使用newlib。他们都使用纯asciiwcs* 函数只是喜欢复制/截断字节并且什么都不做的存根。是的,人们使用纯 ascii 环境。
【解决方案3】:

要检查使用的编码是否与 ascii 兼容,您可以使用 C++11(或 C11)unicode escape sequences 并检查 0x000x7f 范围内的所有 unicode 代码点是否解析为相同的整数值.

constexpr bool is_ascii_compatible()
{
    return '\u0000' == 0x00 && '\u0001' == 0x01 && '\u0002' == 0x02 && '\u0003' == 0x03 &&
           '\u0004' == 0x04 && '\u0005' == 0x05 && '\u0006' == 0x06 && '\u0007' == 0x07 &&
           '\u0008' == 0x08 && '\u0009' == 0x09 && '\u000a' == 0x0a && '\u000b' == 0x0b &&
           '\u000c' == 0x0c && '\u000d' == 0x0d && '\u000e' == 0x0e && '\u000f' == 0x0f &&
           '\u0010' == 0x10 && '\u0011' == 0x11 && '\u0012' == 0x12 && '\u0013' == 0x13 &&
           '\u0014' == 0x14 && '\u0015' == 0x15 && '\u0016' == 0x16 && '\u0017' == 0x17 &&
           '\u0018' == 0x18 && '\u0019' == 0x19 && '\u001a' == 0x1a && '\u001b' == 0x1b &&
           '\u001c' == 0x1c && '\u001d' == 0x1d && '\u001e' == 0x1e && '\u001f' == 0x1f &&
           '\u0020' == 0x20 && '\u0021' == 0x21 && '\u0022' == 0x22 && '\u0023' == 0x23 &&
           '\u0024' == 0x24 && '\u0025' == 0x25 && '\u0026' == 0x26 && '\u0027' == 0x27 &&
           '\u0028' == 0x28 && '\u0029' == 0x29 && '\u002a' == 0x2a && '\u002b' == 0x2b &&
           '\u002c' == 0x2c && '\u002d' == 0x2d && '\u002e' == 0x2e && '\u002f' == 0x2f &&
           '\u0030' == 0x30 && '\u0031' == 0x31 && '\u0032' == 0x32 && '\u0033' == 0x33 &&
           '\u0034' == 0x34 && '\u0035' == 0x35 && '\u0036' == 0x36 && '\u0037' == 0x37 &&
           '\u0038' == 0x38 && '\u0039' == 0x39 && '\u003a' == 0x3a && '\u003b' == 0x3b &&
           '\u003c' == 0x3c && '\u003d' == 0x3d && '\u003e' == 0x3e && '\u003f' == 0x3f &&
           '\u0040' == 0x40 && '\u0041' == 0x41 && '\u0042' == 0x42 && '\u0043' == 0x43 &&
           '\u0044' == 0x44 && '\u0045' == 0x45 && '\u0046' == 0x46 && '\u0047' == 0x47 &&
           '\u0048' == 0x48 && '\u0049' == 0x49 && '\u004a' == 0x4a && '\u004b' == 0x4b &&
           '\u004c' == 0x4c && '\u004d' == 0x4d && '\u004e' == 0x4e && '\u004f' == 0x4f &&
           '\u0050' == 0x50 && '\u0051' == 0x51 && '\u0052' == 0x52 && '\u0053' == 0x53 &&
           '\u0054' == 0x54 && '\u0055' == 0x55 && '\u0056' == 0x56 && '\u0057' == 0x57 &&
           '\u0058' == 0x58 && '\u0059' == 0x59 && '\u005a' == 0x5a && '\u005b' == 0x5b &&
           '\u005c' == 0x5c && '\u005d' == 0x5d && '\u005e' == 0x5e && '\u005f' == 0x5f &&
           '\u0060' == 0x60 && '\u0061' == 0x61 && '\u0062' == 0x62 && '\u0063' == 0x63 &&
           '\u0064' == 0x64 && '\u0065' == 0x65 && '\u0066' == 0x66 && '\u0067' == 0x67 &&
           '\u0068' == 0x68 && '\u0069' == 0x69 && '\u006a' == 0x6a && '\u006b' == 0x6b &&
           '\u006c' == 0x6c && '\u006d' == 0x6d && '\u006e' == 0x6e && '\u006f' == 0x6f &&
           '\u0070' == 0x70 && '\u0071' == 0x71 && '\u0072' == 0x72 && '\u0073' == 0x73 &&
           '\u0074' == 0x74 && '\u0075' == 0x75 && '\u0076' == 0x76 && '\u0077' == 0x77 &&
           '\u0078' == 0x78 && '\u0079' == 0x79 && '\u007a' == 0x7a && '\u007b' == 0x7b &&
           '\u007c' == 0x7c && '\u007d' == 0x7d && '\u007e' == 0x7e && '\u007f' == 0x7f;
}

Demo here.

编辑:

我将对解决方案进行一些扩展,因为它本身有点模糊。正如 KamilCuk 的回答已经说过的那样,在 C 和 C++ 中,可以出现在字符文字中的字符数量有限,所以像 start of heading0x01 in ascii)不能用字符文字(不使用 unicode 转义序列 \u\U),因为它没有转义序列,就像一些特殊字符一样(例如新行 \n)。使用 \x01\001 也不起作用,因为它们只表示一个字节的数据,而不是对应的 ascii 字符。

通过使用 unicode 转义序列,我们可以在所有支持的编码中表示任何 ascii 字符,因为在 0x000x7f 范围内的 unicode 代码点对应于 ascii 中的相同字符。这意味着表达式'\u0041' == 'A' 在任何执行字符集下都必须计算为真。有了这个,我们可以测试所有 ascii 字符是否解析为相同的整数值,就像上面的代码一样。

【讨论】:

    【解决方案4】:

    无需专门检查 ASCII。您真正感兴趣的只是在连续的字母表中获取索引,您可以让语言为您处理:

    int char_index(char c)
    {
        if (c >= 'A' && c <= 'Z')
            return c - 'A';
        return -1;
    }
    

    【讨论】:

    • EBCDIC 没有 A-Z 连续。
    • @Boann 也许,但谁会在 EBCDIC 中编写他们的源代码?如果您以给定的字符集编写源代码,并将编译器配置为将源代码作为该字符集处理,那么您可以编写在该字符集中使用字符文字的代码,并假设该字符集的某些特征,例如字符顺序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-10
    • 2015-09-08
    • 2013-01-29
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多