【问题标题】:iterating through a char array with non standard chars遍历具有非标准字符的 char 数组
【发布时间】:2012-12-14 13:21:48
【问题描述】:

编辑: 我只能使用 stdio.h 和 stdlib.h

我想遍历一个用字符填充的字符数组。

但是像 ä,ö 这样的字符占用了两倍的空间并使用了两个元素。 这就是我的问题所在,我不知道如何访问这些特殊字符。

在我的示例中,字符“ä”将使用 hmm[0] 和 hmm[1]。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char* hmm = "äö";

  printf("%c\n", hmm[0]); //i want to print "ä"

  printf("%i\n", strlen(hmm));

  return 0;
}

谢谢,我尝试在 Eclipse 中运行我附加的代码,它在那里工作。我假设是因为它使用 64 位并且“ä”有足够的空间来容纳。 strlen 确认每个“ä”仅计为一个元素。 所以我想我可以以某种方式告诉它为每个字符分配更多空间(所以“ä”可以适合)?

#include <stdio.h>
#include <stdlib.h>

int main()
{
 char* hmm = "äüö";

  printf("%c\n", hmm[0]);
  printf("%c\n", hmm[1]);
  printf("%c\n", hmm[2]);

  return 0;
}

【问题讨论】:

  • 您正在寻找宽字符。请参阅this answer 以获得一点解释。 wchar_t 是您搜索更多信息的关键字。
  • 如果您不必使用 C,我会使用其他可以更好地处理字符编码和字符串的东西...

标签: c arrays


【解决方案1】:

抱歉拖到这里。虽然我认为强调一些问题很重要。据我了解,OS-X 能够将默认 OS 代码页设置为 UTF-8,因此答案主要是关于在后台使用 UTF-16 的 Windows,其默认 ACP 代码页取决于指定的操作系统区域。

首先你可以打开Character Map,然后发现

两者都位于代码页 1252(西部)中,因此这不是 MBCS 问题。可能是 MBCS 问题的唯一方法是使用 MBCS(Shift-JIS、Big5、Korean、GBK)编码保存文件。

答案,使用
setlocale(LC_ALL, "")

没有深入了解 äö 在命令提示符窗口中错误呈现的原因。

命令提示符确实使用自己的代码页,即 OEM 代码页。 Here 是对其字符映射的以下 (OEM) 代码页的引用。

进入命令提示符并键入以下命令(Chcp) 将显示命令提示符正在使用的当前 OEM 代码页。

使用 setlocal(LC_ALL,"") 遵循 Microsoft 文档,它详细说明了以下行为。

setlocale(LC_ALL, "");
将语言环境设置为默认值,即从操作系统获取的用户默认 ANSI 代码页。

您可以手动执行此操作,方法是使用 chcp 并传递您所需的代码页,然后运行您的应用程序,它应该可以完美地输出文本。

如果这是一个多字节字符集问题,那么将会有其他问题的完整列表:

在 MBCS 下,字符被编码为一个或两个字节。在双字节字符中,第一个或“前导字节”表示它和后面的字节都将被解释为一个字符。第一个字节来自保留用作前导字节的一系列代码。哪些字节范围可以是前导字节取决于使用的代码页。例如,日文代码页 932 使用 0x81 到 0x9F 范围作为前导字节,但韩文代码页 949 使用不同的范围。

查看情况,长度是 4 而不是 2。我会说文件格式已保存为 UTF-8(它实际上可以保存为 UTF-16,尽管您会遇到编译器迟早会出现问题)。您使用的字符不在 0 到 127 的 ASCII 范围内,UTF-8 将 Unicode 代码点编码为两个字节。您的编译器正在打开文件并假定它是您的默认操作系统代码页或 ANSI C。解析字符串时,它会将字符串解释为 ANSI C 字符串 1 字节 = 1 个字符。

为了解决这个问题,在 windows 下将 UTF-8 字符串转换为 UTF-16 并使用 wprintf 打印。目前没有对 Ascii/MBCS stdio 函数的原生 UTF-8 支持。

对于 Mac OS-X,它具有 UTF-8 的默认 OS 代码页,那么我建议遵循 Jonathan Leffler 解决问题的方法,因为它更优雅。但如果稍后将其移植到 Windows,您会发现需要使用下面的示例将字符串从 UTF-8 转换为 UTF-16。

在任一解决方案中,您仍然需要将命令提示符代码页更改为您的操作系统代码页才能正确打印 ASCII 以上的字符。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <locale>

// File saved as UTF-8, with characters outside the ASCII range
int main()
{
    // Set the OEM code page to be the default OS code page
    setlocale(LC_ALL, "");

    // äö reside outside of the ASCII range and in the Unicode code point Western Latin 1
    // Thus, requires a lead byte per unicode code point when saving as UTF-8
    char* hmm = "äö";

    printf("UTF-8 file string using Windows 1252 code page read as:%s\n",hmm);
    printf("Length:%d\n", strlen(hmm));

    // Convert the UTF-8 String to a wide character
    int nLen = MultiByteToWideChar(CP_UTF8, 0,hmm, -1, NULL, NULL);
    LPWSTR lpszW = new WCHAR[nLen];
    MultiByteToWideChar(CP_UTF8, 0, hmm, -1, lpszW, nLen);

    // Print it
    wprintf(L"wprintf wide character of UTF-8 string: %s\n", lpszW); 

    // Free the memory
    delete[] lpszW;

    int c = getchar();
    return 0;
}


UTF-8 file string using Windows 1252 code page read as:äö
Length:4
wprintf wide character of UTF-8 string: äö

【讨论】:

  • 哇,谢谢,这是一些很棒的信息。学到了很多新东西。 :)
【解决方案2】:

您的数据采用多字节编码。因此,您需要使用多字节字符处理技术来划分字符串。例如:

#include <stdio.h>
#include <string.h>
#include <locale.h>

int main(void)
{
    char* hmm = "äö";
    int off = 0;
    int len;
    int max = strlen(hmm);

    setlocale(LC_ALL, "");

    printf("<<%s>>\n", hmm);
    printf("%zi\n", strlen(hmm));

    while (hmm[off] != '\0' && (len = mblen(&hmm[off], max - off)) > 0)
    {
        printf("<<%.*s>>\n", len, &hmm[off]);
        off += len;
    }

    return 0;
}

在我的 Mac 上,它产生了:

<<äö>>
4
<<ä>>
<<ö>>

setlocale() 的调用至关重要;否则,程序会在“C”语言环境中运行,而不是我的 en_US.UTF-8 语言环境和 mblen() 处理不当的事情:

<<äö>>
4
<<?>>
<<?>>
<<?>>
<<?>>

出现问号是因为所打印的字节对于 UTF-8 终端而言是无效的单个字节。

还可以使用宽字符和宽字符打印,如benjarobinanswer..所示。

【讨论】:

  • 谢谢,您的回答很有帮助,很好地解释了问题。
【解决方案3】:

一个字符总是使用一个字节。

在您的情况下,您认为“ä”是一个字符:错误。 使用十六进制查看器打开您的 .c 源代码,您将看到 ä 使用 2 个字符,因为该文件以 UTF8 编码

现在的问题是你想使用宽字符吗?

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

int main()
{
    const wchar_t hmm[] = L"äö";

    setlocale(LC_ALL, "");
    wprintf(L"%ls\n", hmm);
    wprintf(L"%lc\n", hmm[0]);
    wprintf(L"%i\n", wcslen(hmm));

    return 0;
}

【讨论】:

  • 是的,这就是我想要做的。我需要能够遍历它们。
  • 我只能使用stdio.h和stdlib.h,但是在你的例子中它也只显示“?”。
  • 我修复了这个例子:没有 setlocale,wprintf 的输出是 UTF-16 或其他格式(控制台不理解)以及为什么只能使用 stdio.h 和 stdlib.h ?如果是这样,您不能使用宽字符...
【解决方案4】:

我会检查您的命令提示符字体/代码页,以确保它可以显示您的操作系统单字节编码。注意命令提示符有自己的代码页,与您的文本编辑器不同。

【讨论】:

  • 谢谢,刚刚在 eclipse 中运行了,就可以正确显示了。
  • 很高兴听到 :) 祝你好运,并发布您遇到的任何其他问题,我们可以帮助解决。
猜你喜欢
  • 2020-08-19
  • 2014-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-14
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多