【问题标题】:How to correctly count æ ø å (Unicode as UTF-8) characters in C?如何正确计算 C 中的æøå(Unicode 为 UTF-8)字符?
【发布时间】:2014-09-12 08:07:58
【问题描述】:

我正在编写一个简单的程序来计算我放入链接列表的文本文件 (UTF-8) 中的字符。一切似乎都运行良好,除了每次实例计算 æ ø å (挪威字母表中的最后三个字符)两次。所以如果字符串是æøå,我得到 6 而不是 3。如何解决这个问题?

int length()
{
  pointer = root; // Reset pointer
  int i; // Looping through data in node 
  int len = 0; // Counting characters
  int sizedata = sizeof(pointer->data); // Sets size limit for data in node

  while(pointer != NULL)
    {
      for(i = 0; i < sizedata; i++) // Looping through data in node
    {
      if(pointer->data[i] == '\0') break; // Stops count on end of string
      len++; // Counting characters
    }
      pointer = pointer->next; // Linking to next node
    }
  printf("Length of text is: %d characters\n", len);
}

【问题讨论】:

  • 您的文本文件是 UTF-8 格式吗?然后你应该根据它们在 UTF-8 中的长度来推进你的字符。只有 7 位 ASCII 字符占用一个字节;其余的 Latin1 字符占用两个字节。
  • 具体来说,c3 a6 c3 b8 c3 a5 将是 æøå 所需的字节(以防您不知道)。
  • 比如何更重要的是为什么。什么是“性格”?一个字形?代码点?如果是后者,为什么要计算它们?为什么不计算字节数?如果不是字节,那么字形?你想达到什么目的?请参阅utf8everywhere.org,Twitter 如何计算字符数。

标签: c unicode character counting


【解决方案1】:

我根据这个site改了代码。对于len++ 之前的if 语句,一切都是一样的;

int length()
{
    pointer = root; // Reset pointer
    int i; // Looping through data in node 
    int len = 0; // Counting characters
    int sizedata = sizeof(pointer->data); // Sets size limit for data in node

    while(pointer != NULL)
    {
        for(i = 0; i < sizedata; i++) // Looping through data in node
        {
            if(pointer->data[i] == '\0') break; // Stops count on end of string
            if ((pointer->data[i] & 0xC0) != 0x80)  //count characters
                len++;
        }
        pointer = pointer->next; // Linking to next node
    }
    printf("Length of text is: %d characters\n", len);
}

注意(感谢@Eljay): 这是计算 Unicode 代码点(以 UTF-8 编码),而不是字符(字形)。有些字符由多个代码点组成。例如,对于 x 和两个组合码点,x̝̌ 是 78 cc 9d cc 8c。此例程会将 1 个字符计为长度为 3(代码点)。

【讨论】:

  • 这可行,但请记住,它不会检测到损坏的 UTF-8 字符串。
  • 谢谢,现在计数正确。但我真的不明白 ((pointer->data[i] & 0xC0) != 0x80) 实际上做了什么。这里的 0xC0 和 0x80 指的是什么?
  • 请查看发布的链接。
  • 这是计算 Unicode 代码点(以 UTF-8 编码),而不是字符(字形)。有些字符由多个代码点组成。例如,x̝̌78 cc 9d cc 8c,对于 x 和两个组合代码点。此例程会将 1 个字符计为长度 3(代码点)。
【解决方案2】:

您的文本文件似乎以 UTF-8 编码。那么你应该尊重一个字符的编码长度,它可以从一个字节序列的第一个字节推导出来,见this Wikipedia atice

下面的代码只计算单个字符串的长度,即它不使用您的链表结构。

一个字节序列的长度存储在一个数组中,方便查找; -1 标记序列中第一个字节的非法值。标记为cont 的字节是连续字节,仅应出现在格式良好的UTF-8 字符串中序列的第一个字节之后。与这个相比,Igor 的解决方案非常简洁,只是跳过了它们。

我已将 char 指针转换为一个字节(或 &lt;stdint.h&gt; 中的 uint8_t),以保证数组索引为非负数。

这个解决方案可能不必要地冗长,但可以作为尝试解码字符时的起点(而不是仅仅计算它们)。

不管怎样,这里是:

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

static char utf8_len[256] = {
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* cont */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* cont */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* cont */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* cont */
   -1, -1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
    3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
    4,  4,  4,  4,  4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

/*
 *      Return character count of an UTF-8 encodes string; -1 indicates
 *      a decoding error.
 */
int str_length(const char *s)
{
    const uint8_t *p = (const uint8_t *) s;
    int len = 0;

    while (*p) {
        int cl = utf8_len[*p];

        if (cl <= 0) return -1;
        len++;
        p += cl;
    }

    return len;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 2012-04-12
    • 2012-07-02
    • 2021-12-12
    • 2010-09-21
    • 2011-06-27
    相关资源
    最近更新 更多