LC_* 环境变量

参考资料:

LC_* 环境变量用于设置区域信息, 主要包括这些:

高优先级:

  • LC_ALL: 设置此值时, 则会覆盖其余 LC_* 的取值
  • LC_COLLATE: 影响字符的排序规则
  • LC_CTYPE: 影响字符分类(字母,数字,符号等)以及字符集的范围以及对应的字节表示

中优先级:

  • LC_MESSAGES: 控制程序显示的提示和错误信息的语言
  • LC_MONETARY: 控制货币的符号以及货币符号的位置
  • LC_NUMERIC: 控制数字的输出格式(例如每三位用逗号隔开)
  • LC_TIME: 控制日期显示格式

低优先级:

  • LANG: 当 LC_ALL 未被设置时, 且 LC_* 变量未设置时, 那么 LC_* 变量将使用 LANG 的取值

以上这些变量的常见取值有

  • C, POSIX: 这两者完全等价, 字符集仅包括 ASCII, 是最简单, 与区域无关的默认值, 设置此值时在 C 语言环境下可保证一致行为, 也是操作系统启动时的默认值
  • C.UTF-8: 对 C 的扩展, 主要是扩展字符集为 UTF-8, 但与区域无关. 当希望在 C 语言环境下保证一致行为, 且希望采用 UTF-8 字符集时, 推荐采用此值.
  • en_US.utf8: 字符集为 UTF-8, 日期、时间、货币和其他格式符合美国习惯
  • zh_CN.utf8: 字符集为 UTF-8, 日期、时间、货币和其他格式符合中国习惯

上述优先级的设定可以用下面的 python 代码示意:

keys = ["LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME"]
lc_vars = {key: "C" for key in keys}  # C 是默认值
for key in keys:
    if os.environ.get("LC_ALL", ""):
        lc_vars[key] = os.environ.get("LC_ALL", "")
    elif os.environ.get(key, ""):
        lc_vars[key] = os.environ.get(key, "")
    elif os.environ.get("LANG", ""):
        lc_vars[key] = os.environ.get("LANG", "")

除了上述变量以外, 使用 locale 命令, 还会看到这些变量:

  • LANGUAGE 主要用于翻译设置的优先级, 例如: LANGUAGE=fr:en:de, 表明翻译优先级由高到低为: 法语,英语,德语
  • LC_PAPER: 纸张大小
  • LC_NAME: 人名的书写格式
  • LC_ADDRESS: 地址的书写格式
  • LC_TELEPHONE: 电话号码格式
  • LC_MEASUREMENT: 度量衡
  • LC_IDENTIFICATION: 特定标识?

所以, 比较省事的做法是直接设置 LC_ALL 变量. 最后, 以上所有的变量以及优先级仅对严格遵循 POSIX 标准的应用程序有效. 并且这些变量通常是在“软件本地化”(国际化:i18n 和本地化:l10n)的场景下才会用到

以下是一个关于 LC_TIME 的示例

import locale
from datetime import datetime

# 可以尝试设置为: C, C.UTF-8, en_US.UTF-8, zh_CN.UTF-8
locale.setlocale(locale.LC_TIME, 'C')

# 获取并打印当前系统的日期格式
current_locale = locale.getlocale(locale.LC_TIME)
print("当前 LC_TIME 本地化设置:", current_locale)

# 打印当前时间, 注意: 这里用的格式化方式是 %c
now = datetime.now()
print("当前时间:", now.strftime('%c'))

输出:

# 设置为 C 或 POSIX 时
当前 LC_TIME 本地化设置: (None, None)
当前时间: Sun Sep 22 11:22:24 2024

# 设置为 en_US.UTF-8 时
当前 LC_TIME 本地化设置: ('en_US', 'UTF-8')
当前时间: Sun 22 Sep 2024 11:22:51 AM

# 设置为 zh_CN.UTF-8 时
当前 LC_TIME 本地化设置: ('zh_CN', 'UTF-8')
当前时间: 2024年09月22日 星期日 10时59分10秒

# 设置为 C.UTF-8 时, 不同的环境配置可能有所不同
当前 LC_TIME 本地化设置: ('en_US', 'UTF-8')
当前时间: Sun 22 Sep 2024 11:22:51 AM

(1) 注意: 如果出现 locale.Error: unsupported locale setting 这种报错, 可以使用 locale 命令进行检查和添加:

# 查看系统中支持的语言环境
locale -a

# 安装语言环境
sudo locale-gen zh_CN.UTF-8

# 查看所有LC相关的环境变量的设置情况
locale

# Debian/Ubuntu 系统 LC 相关变量的默认值设置文件: /etc/default/locale
# 修改后可以 source 或重新登录使其生效

# 安装/删除语言环境
# 可以直接修改 /etc/locale.gen 文件, 然后执行
sudo locale-gen

(2) 注意: 当设置为 C.UTF-8 时, 显示的值可能会与系统默认的 LC_* 变量有关

Little Endian vs Big Endian

在 C 语言的虚拟内存中, 假设有一个 32 位的整数数组, 用来存储: [1, 2, 3], 假设数组的起始地址是 0x100, 那么无论是 Little Endian 还是 Big Endian, 0x100, 0x101, 0x102, 0x103 这 4 个字节用于存储 1, 0x104, 0x105, 0x106, 0x107 这 4 个字节用于存储 2, 0x108, 0x109, 0x10A, 0x10B 这 4 个字节用于存储 3, 另外在一个字节内部, 总是高位在前, 低位在后

0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B
# Big Endian (按比特值)
00000000, 00000000, 00000000, 00000001
00000000, 00000000, 00000000, 00000002
00000000, 00000000, 00000000, 00000003
# Little Endian (按比特值)
00000001, 00000000, 00000000, 00000000
00000002, 00000000, 00000000, 00000000
00000003, 00000000, 00000000, 00000000

在文件的场景下, 文件在进行网络传输或是U盘拷贝时,其字节顺序不会发生变化(拷贝和传输时,并不知道文件中哪些字节应该组合在一起形成有意义的内容,因此这些过程字节顺序只能是原封不动的). 如何理解文件中的字节由写入和读取程序.

import struct
with open("x.dat", "wb") as fw:
    fw.write(struct.pack("<I", 1))  # b'\x01\x00\x00\x00'
    fw.write(struct.pack(">I", 2))  # b'\x00\x00\x00\x02'

with open("x.dat", "rb") as fr:
    x = fr.read(8)  # b'\x01\x00\x00\x00\x00\x00\x00\x02'
 
struct.unpack("<I", x[:4])  # (1,)
struct.unpack(">I", x[4:])  # (2,)

在上面的例子中, 实际上是写入程序与读取程序约定了如下协议: 文件中包含两个 int32 的数字, 第一个数字采用 Little Endian, 第二个数字采用 Big Endian.

numpy.ndarray.newbyteorder

# S 表示使用相反的字节序
np.array([1, 2], dtype=np.int16).newbyteorder('S')  # array([256, 512], dtype=int16)
# = 和 I 表示使用相同的字节序
np.array([1, 2], dtype=np.int16).newbyteorder('=')  # array([1, 2], dtype=int16)
np.array([1, 2], dtype=np.int16).newbyteorder('I')  # array([1, 2], dtype=int16)

# 以下两个的输出与系统有关, 下面假设系统默认的是 Little Endian
import sys
sys.byteorder  # 'little'
np.array([1, 2], dtype=np.int16).newbyteorder('<')  # array([1, 2], dtype=int16)
np.array([1, 2], dtype=np.int16).newbyteorder('>')  # array([256, 512], dtype=int16)