为OEM和Ansi中的每个区域设置获取代码页。

4

如何获取每个语言环境的代码页(不仅限于我的语言环境)?

我正在寻找一个简单的Python / C# / C函数(最好是Python或C#),以便为我想要的每个语言环境找到Ansi和OEM中的代码页。

1个回答

4

在Windows Vista及以上版本的C语言中,您可以通过GetLocaleInfoEx函数查询区域设置名称的信息。分别用于查询ANSI和OEM代码页的区域设置信息常量LOCALE_IDEFAULTANSICODEPAGE(0x1004)和LOCALE_IDEFAULTCODEPAGE(0x000B)。您可以通过EnumSystemLocalesEx枚举所有系统区域设置名称,并在回调中查询每个区域设置的ANSI和OEM代码页。

在Python脚本中,您可以通过ctypes调用这些函数。例如:

import ctypes
from ctypes import c_int
from ctypes.wintypes import BOOL, DWORD, LPVOID, LPWSTR, LPARAM, WCHAR

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

CP_ACP = 0
CP_OEMCP = 1
LOCALE_NAME_USER_DEFAULT = None
LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale"
LOCALE_RETURN_NUMBER = 0x20000000
LOCALE_IDEFAULTCODEPAGE = 0x0000000B
LOCALE_IDEFAULTANSICODEPAGE = 0x00001004
LOCALE_SENGLISHLANGUAGENAME = 0x00001001
LOCALE_SENGLISHCOUNTRYNAME = 0x00001002

LOCALE_ENUMPROCEX = ctypes.WINFUNCTYPE(BOOL, 
    LPWSTR, # lpLocaleString
    DWORD,  # dwFlags
    LPARAM) # lParam

def _check_zero(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

kernel32.EnumSystemLocalesEx.errcheck = _check_zero
kernel32.EnumSystemLocalesEx.argtypes = (
    LOCALE_ENUMPROCEX, # lpLocaleEnumProcEx
    DWORD,             # dwFlags
    LPARAM,            # lParam
    LPVOID)            # lpReserved

LCTYPE = DWORD
kernel32.GetLocaleInfoEx.errcheck = _check_zero
kernel32.GetLocaleInfoEx.argtypes = (
    LPWSTR, # lpLocaleName,
    LCTYPE, # LCType,
    LPVOID, # lpLCData,
    c_int)  # cchData

def get_language(locale=LOCALE_NAME_SYSTEM_DEFAULT):
    length = kernel32.GetLocaleInfoEx(locale, LOCALE_SENGLISHLANGUAGENAME, 
        None, 0)
    language = (WCHAR * length)()
    kernel32.GetLocaleInfoEx(locale, LOCALE_SENGLISHLANGUAGENAME, 
        language, length)
    return language.value

def get_country(locale=LOCALE_NAME_SYSTEM_DEFAULT):
    length = kernel32.GetLocaleInfoEx(locale, LOCALE_SENGLISHCOUNTRYNAME, 
        None, 0)
    country = (WCHAR * length)()
    kernel32.GetLocaleInfoEx(locale, LOCALE_SENGLISHCOUNTRYNAME, 
        country, length)
    return country.value

def get_acp(locale=LOCALE_NAME_SYSTEM_DEFAULT):
    cp_ansi = DWORD()
    kernel32.GetLocaleInfoEx(locale, LOCALE_IDEFAULTANSICODEPAGE | 
        LOCALE_RETURN_NUMBER, ctypes.byref(cp_ansi), 
        ctypes.sizeof(cp_ansi) // ctypes.sizeof(WCHAR))
    return cp_ansi.value

def get_oemcp(locale=LOCALE_NAME_SYSTEM_DEFAULT):
    cp_oem = DWORD()
    kernel32.GetLocaleInfoEx(locale, LOCALE_IDEFAULTCODEPAGE | 
        LOCALE_RETURN_NUMBER, ctypes.byref(cp_oem), 
        ctypes.sizeof(cp_oem) // ctypes.sizeof(WCHAR))
    return cp_oem.value

def list_system_locales():
    system_locales = []
    @LOCALE_ENUMPROCEX
    def enum_cb(locale, flags, param):
        system_locales.append((locale, 
            get_language(locale), get_country(locale), 
            get_acp(locale), get_oemcp(locale)))
        return True
    kernel32.EnumSystemLocalesEx(enum_cb, 0, 0, None)
    return sorted(system_locales)

请注意,仅使用Unicode的语言环境没有ANSI或OEM代码页。在这种情况下,返回的值是当前系统ANSI和OEM代码页的值,即CP_ACP(0)和CP_OEMCP(1)。例如,印度(IN)的印地语(hi)是一个仅使用Unicode的语言环境:
>>> (get_acp('hi-IN'), get_oemcp('hi-IN')) == (CP_ACP, CP_OEMCP)
True

非常感谢您的关注!有没有办法让list_system_locales返回完整的国家(语言),就像我在Windows区域设置窗口中看到的那样,而不是像ff-NGen-MG这样,而且这个列表对于所有Windows版本都是正确的,还是它取决于某些因素? - g319909.nwytg.coM
EnumSystemLocalesEx 枚举当前系统可用的语言环境;不同的系统可能不一样。此外,枚举可以被缩小范围。我让它枚举所有语言环境,包括具有替代排序的变体。等我有时间时,我会添加一个函数来获取完整的语言和国家名称。 - Eryk Sun
我不知道编码页分配的深层历史,早在1993年的Windows NT 3.1时代。编码页是遗留的区域设置数据,所以我认为它们至少从Windows Vista开始就已经稳定了。我的更新答案需要Windows 7+。语言环境名称是在Vista中引入的,取代了本地ID(LCID),但我用于英语语言和国家名称的信息常量是在Windows 7中引入的。 - Eryk Sun
不是因为你的系统缺少特定的ANSI或OEM代码页而导致“hi-IN”没有定义。再次强调,代码页57002(ISCII Devanagari)的实现与Windows API使用的传统ANSI / OEM系统不兼容,这使得“hi-IN”成为仅支持Unicode的语言环境。在Windows 10中,设置系统语言环境的小程序会发现“hi-IN”是一个仅支持Unicode的语言环境,因此启用了(仍处于测试阶段的)支持将ANSI和OEM代码页设置为65001(UTF-8,一种Unicode编码)的功能。 - Eryk Sun
在Windows 7中,我们无法将系统区域设置为“hi-IN”。正如我所说,它必须设置为其他内容,例如“en-IN”。当您查询“hi-IN”的ANSI代码页时,在所有系统上它都将返回CP_ACP(0)。当您使用此特殊的代码页值与WideCharToMultiByte一起使用时,它会使用系统区域设置的代码页,无论它是什么。您可以通过GetACP进行查询。 - Eryk Sun
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接