严格的C风格字符串 Unicode To UTF-8 的实现(C#、JavaScript)

53 篇文章 2 订阅
9 篇文章 0 订阅

  本文是关于  Unicode 也就是 LPWSTR 转换成 UTF-8 的实现,在 Win32k 平台中我们可以借助 “MultiByteToWideChar / WideCharToMultiByte”【核心编程】两个函数进行多字节与宽字节字符串进行转换【PS:A2W / W2A 无法转换,CP_ACP在WIN32中文版上为936(GBK简体中文)】

  .NET Framework 提供 “System.Text.Encoding.UTF8” 可以进行 .NET 字符串与 UTF8 字符串之间的转换,例:C# 语言执行字符集是 Unicode,并且这是强制性的不存在可以无法更改 “执行字符集” 的说法。

  现代的 C/CXX 语言,都可以利用 “#pragma execution_character_set("utf-8")” 设定执行字符集,但这仅仅只是编译器层面的,而 “工程属性-字符集” 设定它仅仅是影响到几个编译器内部指定的约定宏。

Unicode/UCS-4

bit数

UTF-8

byte数

备注

0000 ~

007F

0~7

0XXX XXXX

1

 

0080 ~

07FF

8~11

110X XXXX

10XX XXXX

2

 

0800 ~

FFFF

12~16

1110XXXX

10XX XXXX

10XX XXXX

3

基本定义范围:0~FFFF

1 0000 ~

1F FFFF

17~21

1111 0XXX

10XX XXXX

10XX XXXX

10XX XXXX

4

Unicode6.1定义范围:0~10 FFFF

20 0000 ~

3FF FFFF

22~26

1111 10XX

10XX XXXX

10XX XXXX

10XX XXXX

10XX XXXX

5

说明:此非unicode编码范围,属于UCS-4 编码

早期的规范UTF-8可以到达6字节序列,可以覆盖到31位元(通用字符集原来的极限)。尽管如此,2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。根据规范,这些字节值将无法出现在合法 UTF-8序列中

400 0000 ~

7FFF FFFF

27~31

1111 110X

10XX XXXX

10XX XXXX

10XX XXXX

10XX XXXX

10XX XXXX

6

   上表是UTF-8字符集编码定义的取值范围, 但事实情况 JavaScript、C# 语言的执行字符集都是 “Unicode” 编码 而不是UTF-8 或许会有人感到奇怪,但本人或许可以理解,一般来说 Unicode 的字符表示范围足够表示目前世界上已知的所有语言的字符,而UTF-8字符集中占用4、5、6字节的字符串你几乎很难遇到它们。

   本文中 C# 的 Unicode To UTF-8 的代码,摘要自本人的 nsjsdotnet 框架,你可以从 “https://github.com/liulilittle/nsjs/blob/master/nsjsdotnet/NSJSString.cs” 中获取到更多与 “UTF8” 字符集相关的内容。

        public static byte[] GetUTF8StringBuffer(string s, out int count)
        {
            count = 0;
            if (s == null)
            {
                throw new ArgumentNullException("s");
            }
            byte[] buf = null;
            int k = 0;
            int i = 0;
            while (i < s.Length)
            {
                char ch = s[i++];
                if (ch < 0x80)
                {
                    k++;
                }
                else if (ch < 0x800)
                {
                    k += 2;
                }
                else if (ch < 0x10000)
                {
                    k += 3;
                }
            }
            buf = new byte[(count = k) + 1];
            fixed (byte* p = buf)
            {
                i = 0;
                k = 0;
                while (i < s.Length)
                {
                    char ch = s[i++];
                    if (ch < 0x80)
                    {
                        p[k++] = (byte)(ch & 0xff);
                    }
                    else if (ch < 0x800)
                    {
                        p[k++] = (byte)(((ch >> 6) & 0x1f) | 0xc0);
                        p[k++] = (byte)((ch & 0x3f) | 0x80);
                    }
                    else if (ch < 0x10000)
                    {
                        p[k++] = (byte)(((ch >> 12) & 0x0f) | 0xe0);
                        p[k++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
                        p[k++] = (byte)((ch & 0x3f) | 0x80);
                    }
                }
            }
            return buf;
        }

  第一个计次循环用于确定转换到UTF-8字符串,所需的字符串缓冲区大小(这是必须的)否则可以采取空间换效率的方式,一个wchar_t 的 Unicode 字符转换成 UTF-8 最大只占三个字节(所以随便浪),但是一个严格的C风格字符串必须携带 “\x0” 所以这就是为什么上面需要在测量的长度上加上 1 的问题,另外根据 “编码规则表” 上定义的说法,<= 0x7F 的字符也就是ASCII字符 都只占用一个字节而不是与 Unicode 一般,不论是不是ASCII字符都需要用双字节进行表示。

var GetUtf8StringBuffer = function (s) {
    'use strict'
    
    var buf = [];
    var k = 0;
    if (!s || typeof s !== 'string') {
        return buf;
    }
    for (var i = 0; i < s.length; i++) {
        var ch = s.charCodeAt(i);
        if (isNaN(ch)) {
            continue;
        }
        if (ch < 0x80) {
            k++;
        }
        else if (ch < 0x800) {
            k += 2;
        }
        else if (ch < 0x10000) {
            k += 3;
        }
    }
    buf = new Array(k + 1);
    buf[k] = 0;
    k = 0;
    for (var i = 0; i < s.length; i++) {
        var ch = s.charCodeAt(i);
        if (isNaN(ch)) {
            continue;
        }
        if (ch < 0x80) {
            buf[k++] = (ch & 0xff);
        }
        else if (ch < 0x800) {
            buf[k++] = ((ch >> 6) & 0x1f) | 0xc0;
            buf[k++] = (ch & 0x3f) | 0x80;
        }
        else if (ch < 0x10000) {
            buf[k++] = ((ch >> 12) & 0x0f) | 0xe0;
            buf[k++] = ((ch >> 6) & 0x3f) | 0x80;
            buf[k++] = (ch & 0x3f) | 0x80;
        }
    }
    return buf;
}

    以下是 JavaScript 语言对于 “utf8” 字符集数组缓冲区转换成 “JavaScript” 的 Local<String> 的实现,(PS:JavaScript 语言默认并不支持UTF8的转换)

var GetStringFromUtf8Buffer = function (utf8) {
    'use strict'

    var s = '';
    if (!utf8 || !(utf8 instanceof Array) || utf8.length <= 0) {
        return s;
    }
    for (var k = 0; k < utf8.length;) {
        var ch = utf8[k++];
        if (ch < 0xE0) {
            ch = (ch & 0x1f) << 6;
            ch |= (utf8[k++] & 0x3f);
        } else if (ch < 0xF0) {
            ch = (ch & 0x0f) << 12;
            ch |= (utf8[k++] & 0x3f) << 6;
            ch |= (utf8[k++] & 0x3f);
        }
        if (ch > 0) {
            s += String.fromCharCode(ch);
        } else { 
            break;
        }
    }
    return s;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值