【问题标题】:Accented/umlauted characters in C?C中的重音/变音符号?
【发布时间】:2010-12-16 01:22:20
【问题描述】:

我刚刚学习 C 并得到了一项任务,我们必须将纯文本翻译成摩尔斯电码并返回。 (我最熟悉 Java,所以请耐心等待我使用的术语)。

为此,我有一个包含所有字母字符串的数组。

char *letters[] = {
".- ", "-... ", "-.-. ", "-.. ", ".", "..-." etc

我写了一个函数来返回所需字母的位置。

int letter_nr(unsigned char c)
{
    return c-97;
}

这是可行的,但分配规范需要处理瑞典变音字母 åäö。瑞典语字母与英语相同,最后是这三个字母。我尝试检查这些,如下所示:

int letter_nr(unsigned char c)
{
    if (c == 'å')
        return 26;
    if (c == 'ä')
        return 27;
    if (c == 'ö')
        return 28;
    return c-97;
}

不幸的是,当我尝试测试这个函数时,这三个函数的值都相同:98。这是我的主要测试函数:

int main()
{   
    unsigned char letter;

    while(1)
    {
        printf("Type a letter to get its position: ");
        scanf("%c", &letter);
        printf("%d\n", letter_nr(letter));
    }
    return 0;
}

我能做些什么来解决这个问题?

【问题讨论】:

  • 我也有 OS X。我的波兰语字母也有同样的问题 :)

标签: c xcode macos character-encoding


【解决方案1】:

字符常量的编码实际上取决于您的语言环境设置。

最安全的选择是使用宽字符,以及相应的函数。您将字母表声明为const wchar_t* alphabet = L"abcdefghijklmnopqrstuvwxyzäöå",将单个字符声明为L'ö';

这个小示例程序适用于我(也适用于 UTF-8 的 UNIX 控制台)- 试试吧。

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

int main(int argc, char** argv)
{
    wint_t letter = L'\0';
    setlocale(LC_ALL, ""); /* Initialize locale, to get the correct conversion to/from wchars */
    while(1)
    {
        if(!letter)
            printf("Type a letter to get its position: ");

        letter = fgetwc(stdin);
        if(letter == WEOF) {
        putchar('\n');
        return 0;
        } else if(letter == L'\n' || letter == L'\r') { 
        letter = L'\0'; /* skip newlines - and print the instruction again*/
        } else {
        printf("%d\n", letter); /* print the character value, and don't print the instruction again */
        }
    }
    return 0;
}

示例会话:

Type a letter to get its position: a
97
Type a letter to get its position: A
65
Type a letter to get its position: Ö
214
Type a letter to get its position: ö
246
Type a letter to get its position: Å
197
Type a letter to get its position: <^D>

我了解在 Windows 上,这不适用于 Unicode BMP 之外的字符,但这不是问题。

【讨论】:

  • 他在 Mac OS X 上。所以控制台已准备好 UTF8,因此语言环境不会影响他的编码。
  • 当然平台很重要 - 'ö' 不适合 UTF-8 中的一个字节,因此您不能将其作为字符常量进行比较。
  • 我最喜欢这个,因为它似乎有效。但是,它给了我两个打印,显然一个用于变音符号 (195),然后另一个,我认为是字母代码。
  • 问题是大多数 pre-Unicode 语言(比如 C)不处理 UTF8 值的 bean。如果我正在设计一种语言,我会将字节与字符分开并构建对标准 Unicode 格式的支持。
  • 在终端中locale charmap的输出是什么,在c程序中调用setlocale()之后调用nl_langinfo(CODESET)的输出是什么?
【解决方案2】:

一般来说,编码的东西是相当复杂的。另一方面,如果你只是想要一个特定于你的编译器/平台的肮脏解决方案,而不是在你的代码中添加这样的东西:

printf("letter 0x%x is number %d\n", letter, letter_nr(letter));

它将为您的变音符号提供十六进制值。不仅仅是在if 语句中用数字替换你的字母。

编辑你说你总是得到 98 所以你的 scanf 从控制台得到了98 + 97 = 195 = 0x3C。根据这个table 0x3C 是Latin1 block 中常见LATIN SMALL LETTER N WITH Something 的UTF8 序列的开始。你在Mac OS X

编辑这是我最后的电话。相当骇人听闻,但它对我有用:)

#include <stdio.h>

// scanf for for letter. Return position in Morse Table. 
// Recognises UTF8 for swedish letters.
int letter_nr()
{
  unsigned char letter;
  // scan for the first time,
  scanf("%c", &letter);
  if(0xC3 == letter)
  {
    // we scanf again since this is UTF8 and two byte encoded character will come
    scanf("%c", &letter);
    //LATIN SMALL LETTER A WITH RING ABOVE = å
    if(0xA5 == letter)
      return 26;
    //LATIN SMALL LETTER A WITH DIAERESIS = ä
    if(0xA4 == letter)
      return 27;
   // LATIN SMALL LETTER O WITH DIAERESIS = ö
    if(0xB6 == letter)
      return 28;

    printf("Unknown letter. 0x%x. ", letter);
    return -1;
  } 
  // is seems to be regular ASCII
  return letter - 97;
 } // letter_nr

int main()
{   
    while(1)
    {
        printf("Type a letter to get its position: ");

        int val = letter_nr();
        if(-1 != val)
          printf("Morse code is %d.\n", val);
        else
          printf("Unknown Morse code.\n");

        // strip remaining new line
    unsigned char new_line;
    scanf("%c", &new_line);         
    }
    return 0;
}

【讨论】:

  • 不幸的是,这似乎给了我和以前一样的问题:这三个字母的十六进制值相同。
  • 你能解释一下你从哪里得到信,我应该把它变成一个全局变量并将读数传递给 letter_nr 函数吗?
  • 这篇文章显示了对 UTF-8 和一般编码的深刻无知。这完全是错误的:两个字节的总和不是 unicode 代码点。 -1
  • 我知道 NOT UNICODE 代码点中两个字节的总和。但是对于某些字母,0x3C 是 UTF-8 中的第一个字符。
  • 抱歉,我删除了我的 -1。但仍然 - 检查字节是否等于 0x3c?请检查它是否> 127!否则,任何不以 0x3c 开头的 UTF-8 序列都会产生狂野的结果,因为序列中的每个字节都将被视为 ASCII。
【解决方案3】:

嗯...起初我会说“有趣”的字符不是chars。您不能将其中一个传递给接受 char 参数的函数并期望它能够工作。

试试这个(添加剩余的位):

char buf[100];
printf("Enter a string with funny characters: ");
fflush(stdout);
fgets(buf, sizeof buf, stdin);
/* now print it, as if it was a sequence of `char`s */
char *p = buf;
while (*p) {
    printf("The character '%c' has value %d\n", *p, *p);
    p++;
}

现在尝试对宽字符进行相同操作:#include &lt;wchar.h&gt; 并将 printf 替换为 wprintffgets 替换为 fgetws,等等...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 2012-11-08
    相关资源
    最近更新 更多