awk 降低以重音符号开头的字符串 - 支持外来字符

use*_*883 2 awk diacritics character-encoding

我有一个文件,其中一行包含此字符串:“\xc3\x81vila”

\n\n

我想得到这个输出:"\xc3\xa1vila"

\n\n

问题是awk 的tolower函数仅在字符串不以重音开头时才起作用,而我必须使用 awk。

\n\n

例如,如果我执行awk \'BEGIN { print tolower("\xc3\x81vila") }\'然后我得到"\xc3\x81vila"而不是"\xc3\xa1vila",这就是我所期望的。

\n\n

但是如果我执行awk \'BEGIN { print tolower("Castell\xc3\xb3n") }\'然后我得到"castell\xc3\xb3n"

\n

mkl*_*nt0 5

为了使给定的awk实现能够正确地处理非 ASCII 字符(外文字母),它必须尊重活动区域设置的字符编码,如(有效)设置中所反映的LC_CTYPE(运行locale以查看它)。

\n\n

如今,大多数语言环境都使用 UTF-8 编码,这是一种多字节按需编码,在 ASCII 范围内为单字节,并使用 2 到 4 个字节来表示所有其他 Unicode 字符。
\n因此,对于awk识别非 ASCII(重音、外文)字母的给定实现,它必须能够将多个字节识别为单个字符

\n\n

在主要的awk实施中,

\n\n
    \n
  • GNU Awk ( ),某些Linux 发行版gawk上的默认设置
  • \n
  • BSD awk,也用于 OS X
  • \n
  • Mawk ( mawk),基于 Debian 的 Linux 发行版(例如 Ubuntu)的默认设置
  • \n
\n\n

只有GNU Awk 可以正确处理 UTF8 编码的字符(如果在语言环境中指定,也可能是任何其他编码):

\n\n
$ echo \xc3\x81vilA | gawk \'{print tolower($0)}\'\n\xc3\xa1vila  # both \xc3\x81 and A lowercased\n
Run Code Online (Sandbox Code Playgroud)\n\n

相反,如果您明确希望将字符处理限制为仅 ASCII,请在前面添加LC_CTYPE=C

\n\n
$ echo \xc3\x81vilA | LC_CTYPE=C gawk \'{print tolower($0)}\'\n\xc3\x81vila  # only ASCII char. A lowercased\n
Run Code Online (Sandbox Code Playgroud)\n\n

实用建议:

\n\n
    \n
  • 确定您的默认实现awk什么,请运行awk --version

    \n\n
      \n
    • 对于 Mawk,您将收到一条错误消息,因为它仅支持使用 打印版本信息-W version,但该错误消息将包含单词mawk
    • \n
  • \n
  • 如果可能,安装并使用 GNU Awk(并可选择将其设为默认值awk);它适用于大多数类 Unix 平台;例如:

    \n\n
      \n
    • 在基于 Debian 的平台(例如 Ubuntu)上:sudo apt-get install gawk
    • \n
    • 在 OS X 上,使用Homebrewbrew install gawk .
    • \n
  • \n
  • 如果必须使用 BSD Awk 或 Mawk,请使用上述LC_CTYPE=C方法确保多字节 UTF-8 字符至少在不进行修改的情况下通过[1],但外来字母不会被识别为字母(因此在这种情况下不会被小写)。

  • \n
\n\n
\n\n

[1] OS X 上的 BSD Awk 和 Mawk(奇怪的是后者不在Linux 上)按如下方式处理 UTF-8 编码字符:

\n\n
    \n
  • 每个字节都被错误地解释为它自己的字符
  • \n
  • 如果忽略高位 后,所得字节值落入 ASCII 大写字母范围内,则将其与原始32字节值相加,得到对应的小写字母。
  • \n
\n\n

在本例中,这意味着:

\n\n
    \n
  • \xc3\x81是 Unicode 代码点U+00C1,其 UTF-8 编码是2 字节序列: 0xC3 0x81

  • \n
  • 0xC3:删除高位 ( 0xC3 & 0x7F) 得到0x43,它被解释为 ASCII 字母C,因此将32( 0x20) 添加到原始值,得到0xE3( 0xC3 + 0x20)。

  • \n
  • 0x81:删除高位 ( 0x81 & 0x7F) 会产生0x1,它不在 ASCII 大写字母 ( 65-90, 0x41-0x5a) 的范围内,因此该字节保持原样。

  • \n
  • 实际上,第一个字节从 修改为0xC30xE3而第二个字节保持不变;由于0xC3 0x81不是正确UTF-8 编码字符,终端将打印?来表示这一点。

  • \n
\n