![]() |
1
cmdOptionKana 4 天前
问了一下 deepseek ,它说:UTF-8 编码的汉字在 Java 中会自动转换为 UTF-16 编码,因此赋值时不会出现问题。
|
3
soulflysimple123 4 天前
char 是 utf-16 编码
|
4
tbc3211 4 天前
魔怔
|
5
zhouyin OP @soulflysimple123
输入法输入的汉字是三个字节 编译器自动把汉字转成 utf16 进行存储了 |
![]() |
7
sagaxu 4 天前
一个 char 不够那就两个啊,code point 是 int 类型,超出 BMP 的字符不能用单个 char 表示,要用两个 char 组合
|
9
zhouyin OP 神奇的是 把变量用 FileWriter.write 方法 写入到文件 还是 3 个字节
|
![]() |
13
dandycheung 4 天前 via Android ![]() 你把语言里类型的字长跟存储时编码的存储方案搞混了。在 Java 语言里,你把一个中文的字符串取一下长度看是不是 1 ,跟 char 能不能对上,结论就出来了。
|
14
zhouyin OP |
![]() |
15
dandycheung 4 天前 via Android ![]() 总之就是,Java 语言用两个字节存储一个 char ,而一个汉字在 UTF-8 编码后有三个字节长,但是它仍然是一个 char ,在 Java 语言中占用两个字节;一个英文字母在 UTF-8 编码后是一个字节长,但它也是一个 char ,在 Java 语言中占用两个字节。
有没搞混无所谓了,你自己判断就好。 |
![]() |
16
wuyiccc 4 天前 ![]() 那你看看这个中文汉字 '𠮷' 还能赋值么
|
![]() |
17
sagaxu 4 天前
|
19
xuld 4 天前
“一个 char 由两个字节组成 但一个 utf8 汉字由三个字节组成”,这句话本身没有问题,但代码里的 char 变量和这句话里的 char 不是一回事.。
字符的本质就是一个整数,比如“我”的编码是 25105 ,几乎所有程序在运行时都会直接存储 25105 。 编译器同理,无论源码里面是什么,用什么编码,最后都会统一解析出 25105 这个数值。 java 的 char 类型本质是一个 16 位整数。char a = '我'; 本质等价于 short a = 25105 。显然没有问题。 所以这个问题其实和“Unicode”、“UTF-8”没有任何关系、更不要去扯什么文件编码,那只会越扯越糊涂。 |
![]() |
20
wuyiccc 4 天前
根据 java 核心技术卷说的,char 类型采用 utf16 编码规则,char 描述 utf16 编码规则中的一个代码单元,一些中文用 utf16 编码规则的时候一部分是占用 2 字节-一个代码单元,一部分是 4 字节,2 个代码单元
|
![]() |
21
moposx 4 天前
因为 char 是 16 位无符号整数,用来表示 UTF-16 码位。而 UTF-16 本身是 2 字节或者 4 字节的变长编码,“我”是在 BMP 里的,所以只需要 2 字节即可表示。如果你从扩展 B 区找一个汉字,就会发现它是不能被赋值给 char 的。
|
![]() |
22
wuyiccc 4 天前 ![]() 补充: char 类型不是采用 utf16 编码规则,而是描述了 UTF-16 编码中的一个代码单元
|
23
zhouyin OP |
25
zhouyin OP @wuyiccc
该字能在 java17 赋值给 char 但只能通过位移得到 2 个有用字节 如果 String.valueOf(a).getBytes("UTF-16") 则得不到有用东西 必须赋值给 String 才能处理 这个字的四个字节在此码表网站显示不出来 https://www.toolhelper.cn/Encoding/UTF16 D842 DFB7 |
27
zhouyin OP @wuyiccc
该字通过 string.getBytes("UTF-8") 得到 4 个字节 其实它在 utf8 下 http://www.mytju.com/classCode/tools/encode_utf8.asp 是 6 个字节 |
29
w568w 4 天前 ![]() 这种涉及具体设计的东西,为什么不直接看文档呢: https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Character.html#unicode
太长不看: char 数据类型基于 Unicode 规范,该规范将字符( characters )定义为固定宽度的 16 位实体。从 U+0000 到 U+FFFF 的字符集有时被称为基本多语言平面 (Basic Multilingual Plane ,BMP)。码位大于 U+FFFF 的字符称为补充字符( supplementary characters )。UTF-16 编码这些补充字符的方式是,利用一对 16 位整数(称为「代用码位」), 第一个来自高代用值范围(\uD800-\uDBFF ),第二个来自低代用值范围(\uDC00-\uDFFF )。 因此,一个 char 值代表基本多语言平面中的一个码位,包括 UTF-16 编码使用的代用码位。为了表示那些在 UTF-16 中需要多码位编码的补充字符们(如部分汉字、符号等),将用 int 类型来代表一个完整 Unicode 码位。 因此,那些接受 char 类型的字符串工具函数,将无法处理补充字符;而接受 int 类型的那些,就可以处理所有字符。 |
![]() |
30
codehz 4 天前 via Android
稍微偏个题
其实你 c 语言里也可以这么写,而且有实际用处(不过一般不是中文,而是四个英文字母组成的字面量,类似 enum State { stop = 'stop' }这样的用法,然后就可以在内存里见到这个字面量了,简易调试的时候很有用(不过有字节序的问题,所以现在也不常用) |
31
w568w 4 天前
@w568w #29 手快发出去了。
再太长不看:char 就是 16 位整数,所以有的字符你无法赋值给 char 。int 则用于代表任意一个 Unicode 字符。Java 在 char[] 和 String 中储存字符串的方式是 UTF-16 编码。 |
![]() |
32
cpstar 4 天前
你们不看字节码么?
这句代码经过编译器之后,就变成了 sipush 20320 ,管你是“你”还是什么,一律按照数字处理的,同理还有 boolean 只有 0 和 1 ,进行比较的时候其实就是判断等于 0 与否。本质都是一个数字,甚至观察 String 的本质,也是一堆 char ,一堆数字。 |
34
w568w 4 天前
@sagaxu #33 这我倒没了解过,有来源吗?我的断言是上面文档里的描述:
> The Java platform uses the UTF-16 representation in char arrays and in the String and StringBuffer classes. |
![]() |
35
sagaxu 4 天前 ![]() |
![]() |
36
my3157 4 天前
大多数语言里面, char 都代表的是 single unicode scalar value, 而 utf8 只是编码规则, 长度是 1-4 bytes(问题中的 '我' 就会编码成 3 个 bytes), 覆盖了 BMP(基本多文种平面), 基本上够 99.99% 的各类用途, 而且 uft8 是兼容 ascii 且大小端无关的, uft16 以以上要考虑 ascii 兼容和大小端的问题
|
37
zhouyin OP @cpstar
这个帖子的初忠是 当前编辑器编码 utf8 输入汉字'你'时 输入了三个字节 E4BDA0 java 编译器隐士地把 utf8 字符字面量转成 utf16 4F60 等于十进制 20320 |
![]() |
40
cpstar 3 天前 ![]() OP 38# 编译器干的不就是这个,读取原始文件,然后进行语法识别和语义识别,判断到给本地变量 a 设置 char ,那就把等号后边的字符(以单引号包住的,前一步语法分析没有问题的)按照文件存储编码或者-encoding 选项进行识别,按数字处理,并根据不同的数字范围来使用不同的指令集,iconst_x bipush sipush ldc 等
|
![]() |
41
9LCRwvU14033RHJo 3 天前
为什么很多人说 char 是 utf-16 编码呢? char 存的是 unicode 不是 utf-8 或者 utf-16 。它能存 65536 个基本多文种平面( BMP )的字符,如果超过这个范围(生僻字)就需要两个 char 才能存得下。
char c = '\u0041'; System.out.println(c); // 输出:我 |
![]() |
42
9LCRwvU14033RHJo 3 天前
|
![]() |
44
llej 3 天前
我超市了一下,在两个字节能够表示的是可以直接这样赋值的,但超出了就会报错。
所以好像没啥问题,赋值中文确实可能出错,只是你的用例没到边界情况 ```java class Main { public static void main(String[] args) { // 创建一个包含超出基本多文种平面( BMP )字符的字符串 char str = '𠜎'; System.out.println("字符串: " + str); } } ``` |
![]() |
45
llej 3 天前
@zhouyin 因为他本来就不是用的 utf8 ,java 用的就是 utf16 呀,这个和你代码文件的编码无关的,假设你使用 gbk2312 来保存你的代码,java 解析加载之后还是按他自己的规则走的。
|
47
WorseIsBetter 3 天前
@codehz #30
但这种写法按标准[^1]会得到一个「实现定义」的值。 > The value of an integer character constant containing more than > one character (e.g., 'ab'), or containing a character or escape > sequence that does not map to a single-byte execution character, > is implementation-defined. 考虑到可移植性,通常不建议使用。 除非你写的代码只应用于特定实现,且该实现对此有明确定义。 比如在 GCC[^2] 中: > The compiler evaluates a multi-character character constant > a character at a time, shifting the previous value left by the > number of bits per target character, and then or-ing in the > bit-pattern of the new character truncated to the width of a > target character. [^1]: ISO/IEC 9899:1999 §6.4.4.4/10 [^2]: https://gcc.gnu.org/onlinedocs/cpp/Implementation-defined-behavior.html --- 声明:本回答并非使用 LLM 生成。 |
![]() |
48
cpstar 3 天前
@llej 44#
复制到 intelliJ 里直接就是两个\u ,在 uestudio 中可以显示“𠜎”,按照 UTF-8 保存,javac -encoding UTF8 编译报错。按照 UTF-16 保存,并且-encoding UTF16 ,同样。 45# 在编译阶段,可以指定代码来识别源代码文件格式。典型的问题就是 Windows 环境如果按照 utf-8 编辑文件,但是手工在 cmd 里编译的话,会按照 GBK 识别文件从而在中文字符上出问题。 |
![]() |
49
dandycheung 3 天前 via Android
@sagaxu 你说的这是另外一件事。Windows 下,一个 Unicode 字符也是两个字节,你说是怎么做到的?当然是用更复杂的其它方法。
|
50
zhouyin OP @cpstar
这种奇怪字符无法显示跟当前终端编码无关 gitbash 是 utf8 也无法显示 System.out.println("" + char ) 除非这个 char 是 65535 里面的 那种生僻字符就无法显示 你如果能显示 是因为 jvm 实现由差别 因为 char 最多只能两个字节 那种生僻字符占 4 个字节 |
![]() |
51
sagaxu 3 天前
@dandycheung 做不到的,10 万+字符携带的信息量,不可能编码进 2 字节中,Windows 一个 Unicode 字符也可能是 4 字节。UTF-16 对应的不是字符,可能是半个字符。
|
![]() |
52
dandycheung 3 天前 via Android
@sagaxu 对,UTF-16 的方法叫“代理对”,surrogate pair ; Windows 的原生 Unicode 方案不是 UTF-16 ,而是叫 UCS2 ,虽然在大部分代码点上跟 UTF-16 重合,但并不完全一样。但是这些,都不影响楼主那个问题应该如何理解。
|
53
iseki 3 天前 via Android ![]() Java 的 char 存储的是 UTF-16 的一个 code unit ,一个不在 BMP 的 code point 在 UTF-16 里是两个 code unit ,所以你没法把这部分字符塞进一个 char 。但是常用汉字都在这个范围。
|
54
zhouyin OP |
![]() |
55
moposx 3 天前
@zhouyin 4 字节定长编码方案很早就有了。虽然简单,但由于空间占用和兼容性两方面的问题没能得到大规模应用。另外 Unicode 的编码空间范围是 0x0000 到 0x10FFFF ,远低于理论上限
|
![]() |
56
glcolof 3 天前
@zhouyin 这得看编辑器内部以什么字符编码工作的,比如在 Windows 平台,Windows 自带的文本输入框内部用的就是 ucs2/utf-16 ,所以输入法输入的也是 ucs2/utf-16 ,一个汉字两个字节。
|
![]() |
59
seyoatda 3 天前
@9LCRwvU14033RHJo #41 首先区分字符集和字符编码。Unicode 是字符集。要存到数据中是要指定编码的。准确的说:Java 中的 char 存放的是用 utf-16 编码的 Unicode 。
|
![]() |
60
Belmode 3 天前
我只能说,基础不牢,地动山摇......
|
61
realJamespond 3 天前
相当于 c++的 wchar ?
|
62
Huelse 3 天前
https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html
char 是存储 unicode 字符而不是 utf-8 字符编码 "which defined characters as fixed-width 16-bit entities"说明是 16 位,同时是 utf-16 编码 大部分中文在 unicode 中都是 2 个字节,少数是 3 个字节,所以存在变长补位的说法 |
63
sofm 3 天前
java 中的 char ,能表示 unicode 中处于基本平面 BMP 的所有字符,从 0-65535 ,合计 65536 个字符,这 65536 个字符包括了 全世界范围内语言的 常用字符,自然也包括中文。 特殊生僻字 不在 char 范围内。
char 的取值范围不能超过 65535 ,ide 会爆红提示错误。 char 表示数字,中文在 unicoe 中也是一个数字编号。 当想 print char 时,会将中文字符,转成 实际的 utf-8 的 3 个字节,如果 terminal 配置的时 utf-8 ,就会将这 3 个字节 整体显示为 一个 中文字符。 |
![]() |
64
lululau 2 天前
|
65
lff0305 2 天前
String 类的方法 codePointCount ,offsetByCodePoints 就是解决这个问题的( unicode 的 2/3/4 字节)
|