JAVA中的字符与代码点

摘要
本文介 Java 平台支持增 字符的方式。增 字符是 Unicode 准中代 点超出 U+FFFF 的字符,因此它 无法在 Java 言中描述 为单 个的 16 体(例如 char 数据 型)。 些字符一般极少用,但是,有些会在 如中文或日文人名中用到,因此,在 东亚 国家,政府 用程序通常会要求支持 些字符。
Java 平台目前正在改 ,以便支持 字符的 理, 这种 进对现 有的 用程序影响微乎其微。新的低 API 在需要 使用 个的字符运行。不 ,大多数文本 API 均使用字符序列,例如 String 或字符数 在, 些均解 释为 UTF-16 序列,而且, API 实现 转变为 正确地 理增 字符。 些改 已融入 Java 2 平台 5.0 版, 准版 (J2SE)
详细 释这 些改 之外,本文同 时为应 用程序 开发 确定和 实现 必要的更改提供指 ,以支持整个 Unicode 字符集的使用。
背景
Unicode 最初 设计 是作 固定 度的 16 位字符 编码 。在 Java 言中,基本数据 char 初衷是通 提供一 种简单 的、能 包含任何字符的数据 型来充分利用 这种设计 点。不 在看来, 16 编码 的所有 65,536 个字符并不能完全表示全世界所有正在使用或曾 使用的字符。于是, Unicode 准已 展到包含多达 1,112,064 个字符。那些超出原来的 16 位限制的字符被称作增 字符。 Unicode 2.0 版是第一个包含启用增 字符 设计 的版本,但是,直到 3.1 版才收入第一批增 字符集。由于 J2SE 5.0 版必 支持 Unicode 4.0 版,因此它必 支持增 字符。
字符的支持也可能会成 为东亚 的一个普遍商 要求。政府 用程序会需要 些增 字符,以正确表示一些包含罕 中文字符的姓名。出版 用程序可能会需要 些增 字符,以表示所有的古代字符和 体字符。中国政府要求支持 GB18030 (一 种对 整个 Unicode 字符集 编码 的字符 编码标 准),因此,如果是 Unicode 3.1 版或更新版本, 将包括增 字符。台湾 CNS-11643 包含的 多字符在 Unicode 3.1 中列 字符。香港政府定 了一 种针对 的字符集,其中的一些字符是 Unicode 中的增 字符。最后,日本的一些供 商正 划利用增 字符空 中大量的 用空 收入 50,000 多个日文 字字符 体,以便从其 有系 迁移至基于 Java 平台的解决方案。
因此, Java 平台不 需要支持增 字符,而且必 使 用程序能 方便地做到 一点。由于增 字符打破了 Java 言的基 础设计 构想,而且可能要求 对编 程模型 行根本性的修改,因此, Java Community Process 召集了一个 ,以期找到一个适当的解决方案。 被称 JSR-204 ,使用 Unicode 增补字符支持的 Java 技术规范请求 号。从技 上来 该专 的决定 适用于 J2SE 平台,但是由于 Java 2 平台企 (J2EE) J2SE 平台的最上 ,因此它可以直接受益,我 期望 Java 2 平台袖珍版 (J2ME) 的配置也采用相同的 设计 方法。
,在了解 JSR-204 确定的解决方案之前,我 需要先理解一些 术语
点、字符 编码 方案、 UTF-16 些是指什
不幸的是,引入增 字符使字符模型 得更加 复杂 了。在 去,我 可以 简单 字符 ,在一个基于 Unicode 境(例如 Java 平台)中,假定字符有 16 位,而 在我 需要更多的 术语 。我 会尽量介 得相 对简单 一些 如需了解所有 详细 讨论 信息,您可以 阅读 Unicode 标准第 2 章 Unicode 术报 17“ 字符编码模型 Unicode 专业 人士可略 所有介 直接参 本部分中的最后定
字符是抽象的最小文本 位。它没有固定的形状(可能是一个字形),而且没有 “A” 是一个字符, “€” (德国、法国和 多其他欧洲国家通用 货币 志)也是一个字符。
字符集是字符的集合。例如, 字字符是中国人最先 明的字符,在中文、日文 文和越南文的 写中使用。
编码 字符集是一个字符集,它 为每 一个字符分配一个唯一数字。 Unicode 准的核心是一个 编码 字符集,字母 “A” 编码为 004116 和字符 “€” 编码为 20AC16 Unicode 准始 使用十六 制数字,而且在 在前面加上前 “U+” ,所以 “A” 编码书 “U+0041”
点是指可用于 编码 字符集的数字。 编码 字符集定 一个有效的代 点范 ,但是并不一定将字符分配 所有 些代 点。有效的 Unicode 点范 U+0000 U+10FFFF Unicode 4.0 将字符分配 一百多万个代 点中的 96,382 点。
字符是代 点在 U+10000 U+10FFFF 的字符,也就是那些使用原始的 Unicode 16 设计 无法表示的字符。从 U+0000 U+FFFF 的字符集有 候被称 基本多 言面 (BMP) 。因此, 一个 Unicode 字符要 属于 BMP ,要 属于增 字符。
字符 编码 方案是从一个或多个 编码 字符集到一个或多个固定 度代 码单 元序列的映射。最常用的代 码单 元是字 ,但是 16 位或 32 位整数也可用于内部 理。 UTF-32 UTF-16 UTF-8 Unicode 准的 编码 字符集的字符 编码 方案。
UTF-32 即将 一个 Unicode 点表示 相同 32 位整数。很明 ,它是内部 理最方便的表达方式,但是,如果作 一般字符串表达方式, 要消耗更多的内存。
UTF-16 使用一个或两个未分配的 16 位代 码单 元的序列 Unicode 编码 U+0000 U+FFFF 编码为 一个相同 16 元。增 字符 编码为 两个代 码单 元,第一个 元来自于高代理 U+D800 U+DBFF ),第二个 元来自于低代理范 U+DC00 U+DFFF )。 在概念上可能看起来 似于多字 节编码 ,但是其中有一个重要区 U+D800 U+DFFF 保留用于 UTF-16 ;没有 分配字符作 点。 意味着, 于一个字符串中的 独的代 码单 元, 件可以 识别 是否 码单 元表示某个 单单 元字符,或者是否 码单 元是某个双 元字符的第一个或第二 元。 相当于某些 传统 的多字 字符 编码 是一个 著的改 ,在 传统 的多字 字符 编码 中,字 节值 0x41 既可能表示字母 “A” ,也可能是一个双字 字符的第二个字
UTF-8 使用一至四个字 的序列 对编码 Unicode 编码 U+0000 U+007F 使用一个字 节编码 U+0080 U+07FF 使用两个字 U+0800 U+FFFF 使用三个字 ,而 U+10000 U+10FFFF 使用四个字 UTF-8 设计 原理 :字 节值 0x00 0x7F 表示代 U+0000 U+007F Basic Latin 字符子集,它 对应 ASCII 字符集)。 些字 节值 不会表示其 他代 点, 一特性使 UTF-8 可以很方便地在 件中将特殊的含 义赋 予某些 ASCII 字符。
下表所示 几个字符不同表达方式的比
Unicode
U+0041
U+00DF
U+6771
U+10400
表示字形
UTF-32 码单
00000041
000000DF
00006771
00010400
UTF-16 码单
0041
00DF
6771
D801
DC00
UTF-8 码单
41
C3
9F
E6
9D
B1
F0
90
90
80

另外,本文在 多地方使用 术语 字符序列或 char 序列概括 Java 2 平台 识别 的所有字符序列的容器: char[] , java.lang.CharSequence 实现 (例如 String ),和 java.text.CharacterIterator 实现
这么 术语 。它 与在 Java 平台中支持增 字符有什 么关 系呢?
Java 平台中增 字符的 设计 方法
JSR-204 作出的主要决定是如何在 Java API 中表示增 字符,包括 个字符和所有形式的字符序列。 并排除了多 方法:
*                   重新定 基本 char ,使其具有 32 位, 这样 也会使所有形式的 char 序列成 UTF-32 序列。
*                   有的 16 char 的基 上, 字符引入一 新的 32 位基本 型(例如, char32 )。所有形式的 Char 序列均基于 UTF-16
*                   有的 16 char 的基 上, 字符引入一 新的 32 位基本 型(例如, char32 )。 String StringBuffer 接受并行 API ,并将它 释为 UTF-16 序列或 UTF-32 序列;其他 char 序列 继续 基于 UTF-16
*                   使用 int 表示增 的代 点。 String StringBuffer 接受并行 API ,并将它 释为 UTF-16 序列或 UTF-32 序列;其他 char 序列 继续 基于 UTF-16
*                   使用代理 char ,表示增 点。所有形式的 char 序列基于 UTF-16
*                   引入一 封装字符的 String StringBuffer 接受新的 API ,并将它 释为 字符的序列。
*                   使用一个 CharSequence 例和一个索引的 合表示代 点。
些方法中,一些在早期就被排除了。例如,重新定 基本 char ,使其具有 32 位, 这对 于全新的平台可能会非常有吸引力,但是, J2SE ,它会与 有的 Java 1 、序列化和其他接口不兼容,更不用 基于 UTF-32 的字符串要使用两倍于基于 UTF-16 的字符串的内存了。添加一 型的 char32 可能会 简单 一些,但是仍然会出 机和序列化方面的 问题 。而且, 言更改通常需要比 API 更改有更 的提前期,因此,前面两 方法会 字符支持 来无法接受的延 了在余下的方法中 筛选 出最 方案, 实现 使用四 不同的方法,在大量 行低 字符 理的代 java.util.regex 包)中 实现 字符支持,并 对这 方法的 易程度和运行表 现进 行了比
确定了一 的方法:
*                   使用基本 int 在低 API 中表示代 点,例如 Character 的静 方法。
*                   将所有形式的 char 序列均解 释为 UTF-16 序列,并促 其在更高 层级 API 中的使用。
*                   提供 API ,以方便在各 char 和基于代 点的表示法之 转换
在需要 ,此方法既能 提供一 概念 明且高效的 个字符表示法,又能 充分利用通 可支持增 字符的 API 。同 字符序列在 个字符上的 用, 一点一般 于国 化的 件很有好
这种 方法中,一个 char 表示一个 UTF-16 码单 元, 这样对 于表示代 点有 并不 用。您会注意到, J2SE 术规 在使用 术语 点和 UTF-16 码单 元(表示法是相 的)以及通用 术语 字符(表示法与 该讨论 没有 系)。 API 通常使用名称 codePoint 描述表示代 点的 int 量,而 UTF-16 码单 元的 型当然 char
将在下面两部分中了解到 J2SE 平台的 实质变 其中一部分介 绍单 个代 点的低 API ,另一部分介 采用字符序列的高 接口。
放的增 字符:基于代 点的 API
新增的低 API 两大 :用于各 char 和基于代 点的表示法之 间转换 的方法和用于分析和映射代 点的方法。
最基本的 转换 方法是 Character.toCodePoint(char high, char low) (用于将两个 UTF-16 码单 转换为 一个代 点)和 Character.toChars(int codePoint) (用于将指定的代 转换为 一个或两个 UTF-16 码单 元,然后封装到一个 char[] 内。不 ,由于大多数情况下文本以字符序列的形式出 ,因此,另外提供 codePointAt codePointBefore 方法,用于将代 点从各 字符序列表示法中提取出来: Character.codePointAt(char[] a, int index) String.codePointBefore(int index) 是两 典型的例子。在将代 点插入字符序列 ,大多数情况下均有一些 针对 StringBuffer StringBuilder appendCodePoint(int codePoint) 方法,以及一个用于提取表示代 点的 int[] String 构建器。
用于分析代 码单 元和代 点的方法有助于 转换过 程: Character 中的 isHighSurrogate isLowSurrogate 方法可以 识别 用于表示增 字符的 char charCount(int codePoint) 方法可以确定是否需要将某个代 转换为 一个或两个 char
但是,大多数基于代 点的方法均能 够对 所有 Unicode 字符 实现 基于 char 的旧方法 BMP 字符所 实现 的功能。 以下是一些典型例子:
*                   Character.isLetter(int codePoint) 可根据 Unicode 识别 字母。
*                   Character.isJavaIdentifierStart(int codePoint) 可根据 Java 范确定代 点是否可以启 动标识
*                   Character.UnicodeBlock.of(int codePoint) 可搜索代 点所属的 Unicode 字符子集。
*                   Character.toUpperCase(int codePoint) 可将 定的代 转换为 其大写等 字符。尽管此方法能 支持增 字符,但是它仍然不能解决根本的 问题 ,即在某些情况下,逐个字符的 转换 无法正确完成。例如,德文字符 “"ß"” 应该转换为 “SS” 需要使用 String.toUpperCase 方法。
注意大多数接受代 点的方法并不 检查给 定的 int 是否 于有效的 Unicode 点范 之内(如上所述,只有 0x0 0x10FFFF 的范 是有效的)。在大多数情况下, 该值 是以确保其有效的方法 生的,在 些低 API 中反 复检查 其有效性可能会 性能造成 面的影响。在无法确保有效性的情况下, 用程序必 使用 Character.isValidCodePoint 方法确保代 点有效。大多数方法 于无效的代 点采取的行 没有特 加以指定,不同的 实现 可能会有所不同。
API 包含 便的方法, 些方法可使用其他低 API 实现 ,但是 组觉 些方法很常用,将它 添加到 J2SE 平台上很有意 。不 也排除了一些建 便方法, 这给 提供了一次展示自己 实现 方法能力的机会。例如, 组经过讨论 ,排除了一 种针对 String 的新构建器( 构建器可以 建一个保持 个代 点的 String )。以下是使 用程序使用 有的 API 提供功能的一 种简 便方法:
/**
 * 含有指定代 点的新 String。
 */
String newString(int codePoint) {
    return new String(Character.toChars(codePoint));
}
您会注意到,在 简单 实现 中, toChars 方法始 终创 建一个中 数列, 数列 使用一次即立即 弃。 如果 方法在您的性能 估中出 ,您可能会希望将其 为针对 普通的情况,即 BMP 字符:
/**
 * 含有指定代 点的新 String
 * 针对 BMP 字符 化的版本。
  */
String newString(int codePoint) {
    if (Character.charCount(codePoint) == 1) {
        return String.valueOf((char) codePoint);
    } else {
        return new String(Character.toChars(codePoint));
    }
}
或者,如果您需要 多个 这样 string 可能希望 写一个重 使用 toChars 方法所使用的数列的通用版本:
/**
 * 一个均含有一个指定
  * 点的新 String。
 * 针对 BMP 字符 化的版本。
 */
String[] newStrings(int[] codePoints) {
    String[] result = new String[codePoints.length];
    char[] codeUnits = new char[2];
    for (int i = 0; i < codePoints.length; i++) {
         int count = Character.toChars(codePoints[i], codeUnits, 0);
         result[i] = new String(codeUnits, 0, count);
    }
    return result;
}
,最 您可能会 发现 ,您需要的          是一个完全不同的解决方案。新的构建器 String(int codePoint) 实际 上建 String.valueOf(char) 的一个基于代 点的 备选 方案。 在很多情况下,此方法用于消息生成的 境,例如:
System.out.println("Character " + String.valueOf(char) + " is invalid.");
新的 格式化 API 支持增 文字,提供一 更加 简单 备选 方案:
System.out.printf("Character %c is invalid.%n", codePoint);
使用此高 API 仅简 捷,而它有很多特殊的 点:它可以避免串 (串 会使消息很 本地化),并将需要移 进资 源包 (resource bundle) 的字符串数量从两个减少到一个。
字符透 :功能增
在支持使用增 字符的 Java 2 平台中的大部分更改没有反映到新的 API 内。一般 期是, 理字符序列的所有接口将以适合其功能的方式 理增 字符。本部分着重 达到此 期所作一些功能增
Java 言中的 标识
Java 范指出所有 Unicode 字母和数字均可用于 标识 符。 多增 字符是字母或数字,因 Java 范已 参照新的基于代 点的方法 行更新,以在 标识 符内定 合法字符。 使用 些新方法,需要 检测标识 符的 javac 编译 器和其他工具都 行了修
内的增 字符支持
J2SE 经过 ,可以通 过现 有接口支持增 字符。 以下是一些例子:
*                   字符串大小写 转换 功能已更新,可以 理增 字符,也可以 实现 Unicode 准中 定的特殊大小写 规则
*                   java.util.regex 包已更新, 这样 模式字符串和目 字符串均可以包含增 字符并将其作 完整 理。
*                   在,在 java.text 包内 行整理 ,会将增 字符看作完整 元。
*                   java.text.Bidi 已更新,可以 理增 字符和 Unicode 4.0 中新增的其他字符。 注意, Cypriot Syllabary 字符子集内的增 字符具有从右至左的方向性。
*                   Java 2D API 内的字体渲染和打印技 经过 ,可以正确渲染和 量包含增 字符的字符串。
*                   Swing 文本 实现 已更新,可以 理包含增 字符的文本。
字符 转换
只有很少的字符 编码 可以表示增 字符。如果是基于 Unicode 编码 (如 UTF-8 UTF-16LE ), 旧版的 J2RE 内的字符 转换 器已 按照正确 理增 字符的方式 实现转换 J2RE 5.0 ,可以表示增 字符的其他 编码 转换 器已更新: GB18030 x-EUC-TW 实现 所有 CNS 11643 面)和 Big5-HKSCS 实现 HKSCS-2001 )。
在源文件内表示增 字符
Java 言源文件中,如果使用可以直接表示增 字符的字符 编码 使用增 字符最 方便。 UTF-8 是最佳的 选择 。在所使用的字符 编码 无法直接表示字符的情况下, Java 言提供一 Unicode 转义 法。此 法没有 经过 ,无法直接表示增 字符。而是使用两个 连续 Unicode 转义 符将其表示 UTF-16 字符表示法中的两个 编码单 元。例如,字符 U+20000 写作 “/uD840/uDC00” 。您也 不愿意探究 转义 序列的含 ;最好是写入支持所需增 字符的 编码 ,然后使用一 工具(如 native2ascii )将其 转换为转义 序列。
憾的是,由于其 编码问题 ,属性文件仍局限于 ISO 8859-1 (除非您的 用程序使用新的 XML 格式)。 意味着您始 须对 字符使用 转义 序列,而且可能要使用不同的 编码进 写,然后使用 native2ascii 的工具 转换
UTF-8
Java 平台 对经 UTF-8 很熟悉,但是, 问题 用程序 开发 在可能包含增 字符的文本和 UTF-8 间进 转换时 需要更加留神。需要特 注意的是,某些 J2SE 接口使用的 编码 UTF-8 相似但与其并不兼容。以前,此 编码 被称 “Java modified UTF-8” Java UTF-8 或( 错误 地)直接称 “UTF-8” J2SE 5.0 ,其 明文档正在更新,此 编码 “modified UTF-8” UTF-8 )。
UTF-8 UTF-8 之所以不兼容,其原因有两点。其一, UTF-8 将字符 U+0000 表示 双字 序列 0xC0 0x80 ,而 UTF-8 使用 节值 0x0 。其二, UTF-8 过对 UTF-16 表示法的两个代理代 码单 编码 表示增 字符 个代理代 码单 元由三个字 来表示,共有六个字 。而 UTF-8 使用 个四字 序列表示整个字符。
Java 机及其附 的接口(如 Java 本机接口、多 工具接口或 Java 文件)在 java.io.DataInput DataOutput 接口和 中使用 UTF-8 实现 或使用 些接口和 ,并 行序列化。 Java 本机接口提供与 UTF-8 间进 转换 的例程。而 UTF-8 String java.io.InputStreamReader OutputStreamWriter java.nio.charset (facility) 以及 多其上 API 提供支持。
由于 UTF-8 准的 UTF-8 不兼容,因此切勿同 使用 版本的 编码 UTF-8 只能与上述的 Java 接口配合使用。在任何其他情况下,尤其 于可能来自非基于 Java 平台的 件的或可能通 编译 的数据流,必 使用 准的 UTF-8 。需要使用 准的 UTF-8 不能使用 Java 本机接口例程与 UTF-8 转换
用程序内支持增 字符
在, 大多数 者来 重要的 问题 是:必 须对应 用程序 行哪些更改才能支持增 字符?
答案取决于在 用程序中 行哪 种类 型的文本 理和使用哪些 Java 平台 API
以各 形式 char 序列( [ char[] java.lang.CharSequence 实现 java.text.CharacterIterator 实现 理文本和 使用接受和退回序列(如 char 序列)的 Java API 用程序,可能根本不需要 行任何更改。 Java 平台 API 实现应该 够处 理增 字符。
于本身解 释单 个字符、将 个字符 Java 平台 API 用能 返回 个字符的方法的 用程序 需要考 虑这 些字符的有效 。在很多情况下,往往不要求支持增 字符。例如,如果某 用程序搜索 char 序列中的 HTML 标记 ,并逐一 检查每 char ,它会知道 标记仅 使用 Basic Latin 字符子集中的字符。如果所搜索的文本含有增 字符, 则这 些字符不会与 标记 字符混淆,因 UTF-16 使用代 码单 元表示增 字符,而代 码单 元的 不会用于 BMP 字符。
只有在某 用程序本身解 释单 个字符、将 个字符 Java 平台 API 用能 返回 个字符的方法且 些字符可能 字符 ,才必 更改 该应 用程序。在提供使用 char 序列的并行 API ,最好 而使用此 API 。在其他情况下,有必要使用新的 API char 和基于代 点的表示法之 间进 转换 ,并 用基于代 点的 API 。当然,如果您 发现 J2SE 5.0 中有更新、更方便的 API ,使您能 支持增 字符并同 时简 化代 (如上 格式化范例 中所述), 没有必要 这样 做。
您可能会犹豫,是将所有文本 转换为 点表示法(即 int[] )然后在 表示法中 理, 是在大多数情况下仍采用 char 序列, 在需要 时转换为 点,两者之 孰劣很 确定。当然, 体来 Java 平台 API char 序列肯定具有一定的 优势 ,而且采用 Java 平台 API 可以 省内存空
于需要与 UTF-8 间进 转换 用程序, 需要 真考 是需要 准的 UTF-8 UTF-8 ,并 针对每种 UTF-8 采用适当的 Java 平台。 经修订的 UTF-8 部分介 绍进 行正确 选择 所需的信息。
使用增 字符 测试应 用程序
经过 前面部分的介 后,无 您是否需要修 订应 用程序, 测试应 用程序是否运行正常始 是一 正确的做法。 于不含有 形用 界面的 用程序,有 在源文件内表示增补字符  的信息有助于 设计测试 用例。以下是有 使用 形用 界面 测试 充信息。
于文本 入, Java 2 SDK 提供用于接受 “/Uxxxxxx” 格式字符串的代 入方法, 里大写的 “U” 表示 转义 序列包含六个十六 制数字,因此允 使用增 字符。小写的 “u” 表示 转义 序列 “/uxxxx” 的原始格式。您可以在 J2SDK demo/jfc/CodePointIM 内找到此 入方法及其 明文档。
于字体渲染,您需要至少能 渲染一些增 字符的字体。其中一 字体 James Kass Code2001 字体,它提供手写体字形(如 Deseret Old Italic )。利用 Java 2D 中提供新功能,您只需将 字体安装到 J2RE lib/fonts/fallback 内即可,然后它可自 添加至在 2D XAWT 渲染 使用的所有 逻辑 字体 无需 编辑 字体配置文件。
至此,您就可以确 ,您的 用程序能 完全支持增 字符了!
结论
字符的支持已 引入 Java 平台,大部分 用程序无需更改代 即可 些字符。解 释单 个字符的 用程序可以在 Character 和多 CharSequence 中使用基于代 点的新 API
 
 
 
以下是 Unicode UTF-8 转换关 系表:

U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
 
Byte 数组转整数:
static int bytes2int(byte[] b)
{
         int mask=0xff;
         int temp=0;
      
   int res=0;
        for(int i=0;i<4;i++){
            res<<=8;
            temp=b[i]&mask;
            res|=temp;
        }
       return res;
}
整数转 byte 数组:
static byte[] int2bytes(int num)
{
       byte[] b=new byte[4];
       int mask=0xff;
       for(int i=0;i<4;i++){
            b[i]=(byte)(num>>>(24-i*8));
       }
      return b;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值