Unicode的一个问题

时间:2021-04-18 20:17:42
Unicode与ANSI的区别是,Unicode无论是什么文字都是用2个byte进行编码。当用VS2010的时候,默认的是Unicode。
string s = "你好",cout << s.size() << endl; 输出是4,没有问题。当string s = “hello”的时候,cout << s.size() << endl;输出的是5,但是照理说,一个英文字符对应2个byte,那结果不应该是输出10吗?

14 个解决方案

#1


"xxx"   这个表示的是多字节, L"xxx"表示的才是宽字符的,另外可以使用_T("xxx" ),这样在多字节下与Unicode下具有不同含义

另外string也需要改成wstring使用的才是Unicode

#2


string和wstring的size()严格来说返回的是字符数,不是字节数。
一个汉字相当于两个英文字符。

#3


引用 楼主 zhangsj1007 的回复:
Unicode无论是什么文字都是用2个byte进行编码。


Unicode 字符一共不止 65536 个,不可能只用 2 字节就能表示所有可能的 Unicode 字符。

像 “ 

#4


引用 3 楼 D41D8CD98F 的回复:
Quote: 引用 楼主 zhangsj1007 的回复:

Unicode无论是什么文字都是用2个byte进行编码。


Unicode 字符一共不止 65536 个,不可能只用 2 字节就能表示所有可能的 Unicode 字符。

像 “ 

生僻字以及之后的内容直接被吞了,看来即使是久经考验的论坛系统也无法应对稍微特殊一点的 Unicode 字符啊

今年的《牛津词典》年度词汇 FACE WITH TEARS OF JOY 其实是一个 Unicode 字符,其编码为 1F602 ,一般而言这种字符就是用 4 字节编码的。

#5


所以微软就是没眼光,ansi到unicode,结果还是不够,linux/unix的utf8,来多少都行

#6


仅供参考:
#pragma comment(lib,"user32")
#pragma comment(lib,"gdi32")
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
extern "C" HWND WINAPI GetConsoleWindow();
void HideTheCursor() {
    CONSOLE_CURSOR_INFO cciCursor;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {
        cciCursor.bVisible = FALSE;
        SetConsoleCursorInfo(hStdOut, &cciCursor);
    }
}
void ShowTheCursor() {
    CONSOLE_CURSOR_INFO cciCursor;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {
        cciCursor.bVisible = TRUE;
        SetConsoleCursorInfo(hStdOut, &cciCursor);
    }
}
int main() {
    HWND  hwnd;
    HDC   hdc;
    HFONT hfont;
    wchar_t wc[2];

    system("color F0");
    system("cls");
    HideTheCursor();
    hwnd  = GetConsoleWindow();
    hdc   = GetDC(hwnd);
    hfont = CreateFont(48,0,0,0,0,0,0,0,GB2312_CHARSET ,0,0,0,0,"宋体-方正超大字符集");
    SelectObject(hdc,hfont);
    wc[0]=0xD854u;
    wc[1]=0xDC00u;
    TextOutW(hdc,10,10,wc,2);
    DeleteObject(hfont);
    ReleaseDC(hwnd,hdc);
    getch();
    system("color 07");
    system("cls");
    ShowTheCursor();
    return 0;
}
#if 0
代理项或代理项对是一对共同表示单个字符的 16 位 Unicode 编码值。需要记住的关键一点是:
代理项对实际上是 32 位单个字符,不能再假定一个 16 位 Unicode 编码值正好映射到一个字符。

使用代理项对
代理项对的第一个值是高代理项,包含介于 U+D800 到 U+DBFF 范围内的 16 位代码值。
该对的第二个值是低代理项,包含介于 U+DC00 到 U+DFFF 范围内的值。通过使用代理项对,
16 位 Unicode 编码系统可以对已由 Unicode 标准定义的一百多万个其他字符 (220) 进行寻址。

在传递给 XmlTextWriter 方法的任何字符串中都可以使用代理项字符。不过,代理项字符在编写的
XML 中应该有效。例如,万维网联合会 (W3C) 建议不允许在元素或属性的名称中使用代理项字符。
如果字符串包含无效的代理项对,则引发异常。

另外,可以使用 WriteSurrogateCharEntity 写出与代理项对相对应的字符实体。字符实体以十六
进制格式写出,并用以下公式生成:

(highChar -0xD800) * 0x400 + (lowChar -0xDC00) + 0x10000

如果字符串包含无效的代理项对,则引发异常。下面的示例显示将代理项对作为输入的 WriteSurrogateCharEntity 方法。

C#复制
 // The following line writes &#x10000.
WriteSurrogateCharEntity ('\uDC00', '\uD800');
下面的示例生成一个代理项对文件,将其加载到 XmlReader 中,并用新的文件名保存文件。
然后,原始文件和新文件被加载回应用程序的 XML 文档对象模型 (DOM) 结构中以进行比较。

C#复制
 char lowChar, highChar;
char [] charArray = new char[10];
FileStream targetFile = new FileStream("SurrogatePair.xml",
      FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

lowChar = Convert.ToChar(0xDC00);
highChar = Convert.ToChar(0xD800);
XmlTextWriter tw = new XmlTextWriter(targetFile, null);
tw.Formatting = Formatting.Indented;
tw.WriteStartElement("root");
tw.WriteStartAttribute("test", null);
tw.WriteSurrogateCharEntity(lowChar, highChar);
lowChar = Convert.ToChar(0xDC01);
highChar = Convert.ToChar(0xD801);
tw.WriteSurrogateCharEntity(lowChar, highChar);
lowChar = Convert.ToChar(0xDFFF);
highChar = Convert.ToChar(0xDBFF);
tw.WriteSurrogateCharEntity(lowChar, highChar);

// Add 10 random surrogate pairs.
// As Unicode, the high bytes are in lower
// memory; for example, word 6A21 as 21 6A.
// The high or low is in the logical sense.
Random random = new Random();
for (int i = 0; i < 10; ++i) {
      lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));
      highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));
      charArray[i] = highChar;
      charArray[++i] = lowChar;
}
tw.WriteChars(charArray, 0, charArray.Length);

for (int i = 0; i < 10; ++i) {
      lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));
      highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));
      tw.WriteSurrogateCharEntity(lowChar, highChar);
}

tw.WriteEndAttribute();
tw.WriteEndElement();
tw.Flush();
tw.Close();

XmlTextReader r = new XmlTextReader("SurrogatePair.xml");

r.Read();
r.MoveToFirstAttribute();
targetFile = new FileStream("SurrogatePairFromReader.xml",
       FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

tw = new XmlTextWriter(targetFile, null);
tw.Formatting = Formatting.Indented;
tw.WriteStartElement("root");
tw.WriteStartAttribute("test", null);
tw.WriteString(r.Value);
tw.WriteEndAttribute();
tw.WriteEndElement();
tw.Flush();
tw.Close();

// Load both result files into the DOM and compare.
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
doc1.Load("SurrogatePair.xml");
doc2.Load("SurrogatePairFromReader.xml");
if (doc1.InnerXml != doc2.InnerXml) {
      Console.WriteLine("Surrogate Pair test case failed");
}
在使用 WriteChars 方法(一次写出一个缓冲区的数据)写出时,输入中的代理项对可能
会在一个缓冲区内被意外拆分。由于代理项值是定义完善的,如果 WriteChars 遇到来自
较低范围或者较高范围的 Unicode 值,它将该值标识为代理项对的一半。当遇到
 WriteChars 将导致从拆分代理项对的缓冲区写入的情况时,将引发异常。使用
  IsHighSurrogate 方法检查缓冲区是否以高代理项字符结束。如果缓冲区中的最后一个
  字符不是高代理项,可以将该缓冲区传递给 WriteChars 方法。

请参见
概念
使用 XmlTextWriter 创建格式正确的 XML
XmlTextWriter 的 XML 输出格式设置
XmlTextWriter 的命名空间功能

#endif

#7


UCS-2才是固定2个字节的, UTF-16是变长的.

"UTF-16 developed from an earlier fixed-width 16-bit encoding known as UCS-2 (for 2-byte Universal Character Set) once it became clear that a fixed-width 2-byte encoding could not encode enough characters to be truly universal."
--https://en.wikipedia.org/wiki/UTF-16

#8


当用VS2010的时候,默认的是Unicode

这个只是说,windows的API用的Unicode版本的而已。
跟c++的标准库半毛钱关系都没有。

c++string就是对应ANSI
c++wstring对应的是宽字符。

#include "stdafx.h"
#include <string>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[])
{
wstring s = L"你好";
cout << s.size() << endl;

wstring s1 = L"hello";
cout << s1.size() << endl;

system("pause");
return 0;
}

输出结果是2, 5。
证明size只是对应的字符(宽字符)的个数。

还有,如果编译器改变一个选项,能够改变标准库同一个函数不同的含义。
那这个就不是标准库了。。。。。。


这个是历史遗留问题,微软一开始用的ANSI,发现后来不够用了。
使用Unicode重写windows API,但是函数名称又想不变,所以就有了编译器的选项了。

编译器的Unicode设置,只是打开一个宏开关而已。其它的别想多了。
c++标准库还没有被微软收购呢。。。。
#ifdef UNICODE
#define OutputDebugString  OutputDebugStringW
#else
#define OutputDebugString  OutputDebugStringA
#endif // !UNICODE



#9


Unicode 16,与32 
当初提出Unicode 的时候,只有16Bits
 每个字符都是16Bits的,当初认为可以表示一切字符了
不过后来又产生了32Bits的Unicode
于是 16 Bits 也不能表示了,于是就有了两个字16Bits,表示32Bits的方法,
事情是一点点的积累下来的
当初对的,如今未必就对,就像千年虫一样。

#10


utf16是unicode第一版
所以utf8在旧的windows中没有被广泛采用 包括软件和开发工具
根本就没有非常复杂的原因
也因此 你只要采用新版的os 新版的vc 就没这个困扰了
新版的os中 你用记事本保存文件 可以把encode选为utf8
新版的vc 使用了新的c++标准 char就等于utf8

#11


引用 7 楼 ipqtjmqj 的回复:
UCS-2才是固定2个字节的, UTF-16是变长的.

"UTF-16 developed from an earlier fixed-width 16-bit encoding known as UCS-2 (for 2-byte Universal Character Set) once it became clear that a fixed-width 2-byte encoding could not encode enough characters to be truly universal."
--https://en.wikipedia.org/wiki/UTF-16


是的,
utf-16是变长!!!

同时候,楼上的说法有问题,一个汉字不是2个字节,这个和编码有关系,比如utf8就不是。

utf-8是1-6个字节,

一个汉字2个字节, 估计是一些中国人的c/c++教材里的吧 ,  vc的一些老版本似乎是gb2312编码,导致

汉字2个字节。

另外: gb2312不属于unicode字符集。

#12


该回复于2016-02-03 09:28:05被管理员删除

#13


Unicode的一个问题

#14


string的size方法是已经对字符集进行封装了的,你可以用char,wchar就可以看出来他们大小是不一样的 Unicode的一个问题

#1


"xxx"   这个表示的是多字节, L"xxx"表示的才是宽字符的,另外可以使用_T("xxx" ),这样在多字节下与Unicode下具有不同含义

另外string也需要改成wstring使用的才是Unicode

#2


string和wstring的size()严格来说返回的是字符数,不是字节数。
一个汉字相当于两个英文字符。

#3


引用 楼主 zhangsj1007 的回复:
Unicode无论是什么文字都是用2个byte进行编码。


Unicode 字符一共不止 65536 个,不可能只用 2 字节就能表示所有可能的 Unicode 字符。

像 “ 

#4


引用 3 楼 D41D8CD98F 的回复:
Quote: 引用 楼主 zhangsj1007 的回复:

Unicode无论是什么文字都是用2个byte进行编码。


Unicode 字符一共不止 65536 个,不可能只用 2 字节就能表示所有可能的 Unicode 字符。

像 “ 

生僻字以及之后的内容直接被吞了,看来即使是久经考验的论坛系统也无法应对稍微特殊一点的 Unicode 字符啊

今年的《牛津词典》年度词汇 FACE WITH TEARS OF JOY 其实是一个 Unicode 字符,其编码为 1F602 ,一般而言这种字符就是用 4 字节编码的。

#5


所以微软就是没眼光,ansi到unicode,结果还是不够,linux/unix的utf8,来多少都行

#6


仅供参考:
#pragma comment(lib,"user32")
#pragma comment(lib,"gdi32")
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
extern "C" HWND WINAPI GetConsoleWindow();
void HideTheCursor() {
    CONSOLE_CURSOR_INFO cciCursor;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {
        cciCursor.bVisible = FALSE;
        SetConsoleCursorInfo(hStdOut, &cciCursor);
    }
}
void ShowTheCursor() {
    CONSOLE_CURSOR_INFO cciCursor;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {
        cciCursor.bVisible = TRUE;
        SetConsoleCursorInfo(hStdOut, &cciCursor);
    }
}
int main() {
    HWND  hwnd;
    HDC   hdc;
    HFONT hfont;
    wchar_t wc[2];

    system("color F0");
    system("cls");
    HideTheCursor();
    hwnd  = GetConsoleWindow();
    hdc   = GetDC(hwnd);
    hfont = CreateFont(48,0,0,0,0,0,0,0,GB2312_CHARSET ,0,0,0,0,"宋体-方正超大字符集");
    SelectObject(hdc,hfont);
    wc[0]=0xD854u;
    wc[1]=0xDC00u;
    TextOutW(hdc,10,10,wc,2);
    DeleteObject(hfont);
    ReleaseDC(hwnd,hdc);
    getch();
    system("color 07");
    system("cls");
    ShowTheCursor();
    return 0;
}
#if 0
代理项或代理项对是一对共同表示单个字符的 16 位 Unicode 编码值。需要记住的关键一点是:
代理项对实际上是 32 位单个字符,不能再假定一个 16 位 Unicode 编码值正好映射到一个字符。

使用代理项对
代理项对的第一个值是高代理项,包含介于 U+D800 到 U+DBFF 范围内的 16 位代码值。
该对的第二个值是低代理项,包含介于 U+DC00 到 U+DFFF 范围内的值。通过使用代理项对,
16 位 Unicode 编码系统可以对已由 Unicode 标准定义的一百多万个其他字符 (220) 进行寻址。

在传递给 XmlTextWriter 方法的任何字符串中都可以使用代理项字符。不过,代理项字符在编写的
XML 中应该有效。例如,万维网联合会 (W3C) 建议不允许在元素或属性的名称中使用代理项字符。
如果字符串包含无效的代理项对,则引发异常。

另外,可以使用 WriteSurrogateCharEntity 写出与代理项对相对应的字符实体。字符实体以十六
进制格式写出,并用以下公式生成:

(highChar -0xD800) * 0x400 + (lowChar -0xDC00) + 0x10000

如果字符串包含无效的代理项对,则引发异常。下面的示例显示将代理项对作为输入的 WriteSurrogateCharEntity 方法。

C#复制
 // The following line writes &#x10000.
WriteSurrogateCharEntity ('\uDC00', '\uD800');
下面的示例生成一个代理项对文件,将其加载到 XmlReader 中,并用新的文件名保存文件。
然后,原始文件和新文件被加载回应用程序的 XML 文档对象模型 (DOM) 结构中以进行比较。

C#复制
 char lowChar, highChar;
char [] charArray = new char[10];
FileStream targetFile = new FileStream("SurrogatePair.xml",
      FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

lowChar = Convert.ToChar(0xDC00);
highChar = Convert.ToChar(0xD800);
XmlTextWriter tw = new XmlTextWriter(targetFile, null);
tw.Formatting = Formatting.Indented;
tw.WriteStartElement("root");
tw.WriteStartAttribute("test", null);
tw.WriteSurrogateCharEntity(lowChar, highChar);
lowChar = Convert.ToChar(0xDC01);
highChar = Convert.ToChar(0xD801);
tw.WriteSurrogateCharEntity(lowChar, highChar);
lowChar = Convert.ToChar(0xDFFF);
highChar = Convert.ToChar(0xDBFF);
tw.WriteSurrogateCharEntity(lowChar, highChar);

// Add 10 random surrogate pairs.
// As Unicode, the high bytes are in lower
// memory; for example, word 6A21 as 21 6A.
// The high or low is in the logical sense.
Random random = new Random();
for (int i = 0; i < 10; ++i) {
      lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));
      highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));
      charArray[i] = highChar;
      charArray[++i] = lowChar;
}
tw.WriteChars(charArray, 0, charArray.Length);

for (int i = 0; i < 10; ++i) {
      lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));
      highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));
      tw.WriteSurrogateCharEntity(lowChar, highChar);
}

tw.WriteEndAttribute();
tw.WriteEndElement();
tw.Flush();
tw.Close();

XmlTextReader r = new XmlTextReader("SurrogatePair.xml");

r.Read();
r.MoveToFirstAttribute();
targetFile = new FileStream("SurrogatePairFromReader.xml",
       FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);

tw = new XmlTextWriter(targetFile, null);
tw.Formatting = Formatting.Indented;
tw.WriteStartElement("root");
tw.WriteStartAttribute("test", null);
tw.WriteString(r.Value);
tw.WriteEndAttribute();
tw.WriteEndElement();
tw.Flush();
tw.Close();

// Load both result files into the DOM and compare.
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
doc1.Load("SurrogatePair.xml");
doc2.Load("SurrogatePairFromReader.xml");
if (doc1.InnerXml != doc2.InnerXml) {
      Console.WriteLine("Surrogate Pair test case failed");
}
在使用 WriteChars 方法(一次写出一个缓冲区的数据)写出时,输入中的代理项对可能
会在一个缓冲区内被意外拆分。由于代理项值是定义完善的,如果 WriteChars 遇到来自
较低范围或者较高范围的 Unicode 值,它将该值标识为代理项对的一半。当遇到
 WriteChars 将导致从拆分代理项对的缓冲区写入的情况时,将引发异常。使用
  IsHighSurrogate 方法检查缓冲区是否以高代理项字符结束。如果缓冲区中的最后一个
  字符不是高代理项,可以将该缓冲区传递给 WriteChars 方法。

请参见
概念
使用 XmlTextWriter 创建格式正确的 XML
XmlTextWriter 的 XML 输出格式设置
XmlTextWriter 的命名空间功能

#endif

#7


UCS-2才是固定2个字节的, UTF-16是变长的.

"UTF-16 developed from an earlier fixed-width 16-bit encoding known as UCS-2 (for 2-byte Universal Character Set) once it became clear that a fixed-width 2-byte encoding could not encode enough characters to be truly universal."
--https://en.wikipedia.org/wiki/UTF-16

#8


当用VS2010的时候,默认的是Unicode

这个只是说,windows的API用的Unicode版本的而已。
跟c++的标准库半毛钱关系都没有。

c++string就是对应ANSI
c++wstring对应的是宽字符。

#include "stdafx.h"
#include <string>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[])
{
wstring s = L"你好";
cout << s.size() << endl;

wstring s1 = L"hello";
cout << s1.size() << endl;

system("pause");
return 0;
}

输出结果是2, 5。
证明size只是对应的字符(宽字符)的个数。

还有,如果编译器改变一个选项,能够改变标准库同一个函数不同的含义。
那这个就不是标准库了。。。。。。


这个是历史遗留问题,微软一开始用的ANSI,发现后来不够用了。
使用Unicode重写windows API,但是函数名称又想不变,所以就有了编译器的选项了。

编译器的Unicode设置,只是打开一个宏开关而已。其它的别想多了。
c++标准库还没有被微软收购呢。。。。
#ifdef UNICODE
#define OutputDebugString  OutputDebugStringW
#else
#define OutputDebugString  OutputDebugStringA
#endif // !UNICODE



#9


Unicode 16,与32 
当初提出Unicode 的时候,只有16Bits
 每个字符都是16Bits的,当初认为可以表示一切字符了
不过后来又产生了32Bits的Unicode
于是 16 Bits 也不能表示了,于是就有了两个字16Bits,表示32Bits的方法,
事情是一点点的积累下来的
当初对的,如今未必就对,就像千年虫一样。

#10


utf16是unicode第一版
所以utf8在旧的windows中没有被广泛采用 包括软件和开发工具
根本就没有非常复杂的原因
也因此 你只要采用新版的os 新版的vc 就没这个困扰了
新版的os中 你用记事本保存文件 可以把encode选为utf8
新版的vc 使用了新的c++标准 char就等于utf8

#11


引用 7 楼 ipqtjmqj 的回复:
UCS-2才是固定2个字节的, UTF-16是变长的.

"UTF-16 developed from an earlier fixed-width 16-bit encoding known as UCS-2 (for 2-byte Universal Character Set) once it became clear that a fixed-width 2-byte encoding could not encode enough characters to be truly universal."
--https://en.wikipedia.org/wiki/UTF-16


是的,
utf-16是变长!!!

同时候,楼上的说法有问题,一个汉字不是2个字节,这个和编码有关系,比如utf8就不是。

utf-8是1-6个字节,

一个汉字2个字节, 估计是一些中国人的c/c++教材里的吧 ,  vc的一些老版本似乎是gb2312编码,导致

汉字2个字节。

另外: gb2312不属于unicode字符集。

#12


该回复于2016-02-03 09:28:05被管理员删除

#13


Unicode的一个问题

#14


string的size方法是已经对字符集进行封装了的,你可以用char,wchar就可以看出来他们大小是不一样的 Unicode的一个问题