【问题标题】:Is there a strlen() that works with char16_t?是否有适用于 char16_t 的 strlen()?
【发布时间】:2013-01-09 19:37:02
【问题描述】:

正如问题所说:

typedef __CHAR16_TYPE__ char16_t; 

int main(void)
{
  static char16_t test[] = u"Hello World!\n";

  printf("Length = %d", strlen(test)); // strlen equivalent for char16_t ???

  return 0;
}

我搜索并发现只有 C++ 解决方案。

我的编译器是GCC 4.7

编辑:

为了澄清,我正在寻找一个返回 code points 计数的解决方案,而不是 characters 的计数。

对于包含BMP 之外的字符的UTF-16 字符串,这两者完全不同。

【问题讨论】:

  • 可能,值得自己写?
  • C11 没有为新的字符类型指定这样的实用函数。因为有 C++ 的模板,才有了 C++ 的解决方案。
  • 如果您使用-fshort-wcharwcslen(3) 可能会起作用。
  • @Carl:我认为这是个坏主意。编译器选项不能更改库函数。更糟糕的是,当编译器内联内置版本并在调用 lib 函数时失败...
  • 嗯,编译器驱动程序通常用作链接器前端,因此可以使其工作。不过,它不在我的机器上。我不得不同意这可能是个坏主意。

标签: c unicode c11


【解决方案1】:

std::char_traits 有这个。

#include <string>

std::char_traits<char16_t>::length(yourchar16pointerhere);

【讨论】:

  • 此问题未标记为 c++
【解决方案2】:
#include <string.h>
#include <wchar.h>
#include <uchar.h>

#define char8_t char
#define strlen8 strlen
#define strlen16 strlen16
#define strlen32(s) wcslen((const wchar_t*)s)

static inline size_t strlen16(register const char16_t * string) {
    if (!string) return 0;
    register size_t len = 0;
    while(string[len++]);
    return len;
}

您应该期望返回的 char16_t 字符数,而不是字节数。

优化的 32 位 Intel Atom 组装视图:

gcc -Wpedantic -std=iso9899:2011 -g3 -O2 -MMD -faggressive-loop-optimizations -fkeep-inline-functions -march=atom -mtune=atom -fomit-frame-pointer -mssse3 -mieee-fp -mfpmath=sse -fexcess-precision=fast -mpush-args -mhard-float -fPIC ...

.Ltext0:
    .p2align 4,,15
    .type   strlen16, @function
strlen16:
.LFB20:
    .cfi_startproc
.LVL0:
    mov edx, DWORD PTR 4[esp]
    xor eax, eax
    test    edx, edx
    je  .L4
    .p2align 4,,15
.L3:
.LVL1:
    lea eax, 1[eax]
.LVL2:
    cmp WORD PTR -2[edx+eax*2], 0
    jne .L3
    ret
.LVL3:
    .p2align 4,,7
    .p2align 3
.L4:
    ret
    .cfi_endproc
.LFE20:
    .size   strlen16, .-strlen16

这是一个英特尔反汇编:

static inline size_t strlen16(register const char16_t * string) {
   0:   8b 54 24 04             mov    edx,DWORD PTR [esp+0x4]
    if (!string) return 0;
   4:   31 c0                   xor    eax,eax
   6:   85 d2                   test   edx,edx
   8:   74 16                   je     20 <strlen16+0x20>
   a:   8d b6 00 00 00 00       lea    esi,[esi+0x0]
    register size_t len = 0;
    while(string[len++]);
  10:   8d 40 01                lea    eax,[eax+0x1]
  13:   66 83 7c 42 fe 00       cmp    WORD PTR [edx+eax*2-0x2],0x0
  19:   75 f5                   jne    10 <strlen16+0x10>
  1b:   c3                      ret    
  1c:   8d 74 26 00             lea    esi,[esi+eiz*1+0x0]
    return len;
}
  20:   c3                      ret    
  21:   eb 0d                   jmp    30 <AnonymousFunction0>
  23:   90                      nop
  24:   90                      nop
  25:   90                      nop
  26:   90                      nop
  27:   90                      nop
  28:   90                      nop
  29:   90                      nop
  2a:   90                      nop
  2b:   90                      nop
  2c:   90                      nop
  2d:   90                      nop
  2e:   90                      nop
  2f:   90                      nop

【讨论】:

  • 如果code 对您不起作用,请将您的 C 编译器扔进垃圾桶。
  • 如果此代码试图模仿strlen(),则返回的值太大了。
【解决方案3】:

这是你的基本 strlen:

int strlen16(const char16_t* strarg)
{
   int count = 0;
   if(!strarg)
     return -1; //strarg is NULL pointer
   char16_t* str = strarg;
   while(*str)
   {
      count++;
      str++;
   }
   return count;
}

这里有一个更高效和流行的strlen:

int strlen16(const char16_t* strarg)
{
   if(!strarg)
     return -1; //strarg is NULL pointer
   char16_t* str = strarg;
   for(;*str;++str)
     ; // empty body
   return str-strarg;
}

希望这会有所帮助。

警告:这在计算 UTF-16 字符串的字符(不是代码点)时无法正常工作。当__STDC_UTF_16__ 定义为1 时尤其如此。

UTF-16 是可变长度的(BMP 中每个字符 2 个字节或 BMP 外每个字符 4 个字节),这些函数未涵盖。

【讨论】:

  • 我想你的意思是while(*str)
  • 没有必要保持单独的计数。最后你可以简单地返回str - strarg
  • Null 检查不需要或有用。
  • @Wiz 这取决于您对 unicode 字符串的 strlen 的期望。我接受了这个答案,因为它可以满足我的要求。不过,您是对的,应该注意代理。
  • strlen() 返回size_t。为什么在这里使用int
【解决方案4】:

在 Windows 上,有wcslen()

不管是什么平台,最好不要使用char16_t。我相信这是标准委员会的blunder 将其写入该语言。

【讨论】:

  • wcslen() 适用于 wchar_t,而不是 char16_t。拥有一个表示 UTF-16 代码单元的标准类型怎么会是一个错误?
  • 首先,在 Windows(这是函数所在的位置)上,它实际上是相同的。您可以将一个指针投射到另一个。其次,上面的“错误”一词是一个超链接。欢迎关注链接:)
  • 即使在 Windows 上,这些类型之间的转换也违反了严格的别名规则。 “错误”链接到 UTF-8 Everywhere 页面,我当然同意在任何地方使用 UTF-8 是最好的,但它并不认为不应该有标准的方式来表示 UTF-16代码单元。
  • 我不同意到处使用 UTF-8 是“最好的”,因为最好的不取决于编译器、库或 C 标准,而是取决于具体的用例。我真的很喜欢新的 char16_t 类型的想法,遗憾的是库支持与 char 或 wchar_t 不相称。
【解决方案5】:

您需要读取 2 个字节并检查它们是否都为零,因为在 unicode 中第一个字节可以为零。

不是一个完美的解决方案(实际上是一种奇怪的解决方案):

size_t strlen16(const char16_t* str16) {
    size_t result = 0;
    char* strptr = (char*) str16;
    char byte0, byte1;

    if(str16 == NULL) return result;

    byte0 = *strptr;
    byte1 = *(strptr + 1);

    while(byte0|byte1) {
        strptr += 2;
        byte0 = *strptr;
        byte1 = *(strptr + 1);
        result++;
    }
    return result;
}

【讨论】:

  • 你不需要显式检查每个字节,你可以简单地检查整个char16_t是否等于0; x == 0x == u'\0'。或者,如果表达式在转换为 bool 的上下文中,您可以依赖 u'\0' 转换为 false 的事实; while(*str16) str16++;
猜你喜欢
  • 2011-08-14
  • 2013-11-18
  • 1970-01-01
  • 2018-12-24
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
相关资源
最近更新 更多