找回密码
 立即注册
首页 业界区 业界 [计算机组成原理] 字符集编码: Unicode 字符集(UTF8/UTF ...

[计算机组成原理] 字符集编码: Unicode 字符集(UTF8/UTF16/UTF32)

懵诬哇 3 天前



  • 最近解析 以太网 SOME/IP 报文时,涉及到解析数据类型为 String(标准的Unicode字符集编码的文本序列,且细分为 动态 String [存在 32 bit 的 StringFieldLength] 和 静态 String ) 的场景。
尤其是 Struct 嵌套 String 时,还涉及到 CPU 的内存对齐(alignment)问题。


  • 言归正传,本文对 Unicode 字符集 做个总结。
概述: Unicode字符集

Unicode 字符集的 BOM := Byte Order Mark := 字符顺序标记


  • BOM(Byte Order Mark)在分析unicode之前,先把bom(byte order mark)说一下。
bom是unicode字符顺序的标识符号,一般以魔数(magic code)的形式出现在以Unicode字符编码的文件的开始的头部,作为该文件的编码标识。


  • 举个很简单的例子:
在 windows 下新建一个文本文件,并另存为 utf8 的文件格式。
该文件里面没有任何内容,我们再用Hex Edit来查看该文件的二进制内容:



UTF-8


UTF-8 with BOM
0xEF BB BF就是这个文件的bom, 这也就是标识该文件是以utf8为编码格式的。
带 BOM 的 Unicode 文本 vs. 不带 BOM 的 Unicode 文本


  • UTF-8、UTF-16、UTF-32 还区分带 BOM 的以及不带 BOM 的 Unicode 文本。
  • BOM 的全称为 byte-order mark,即字节顺序标记,它是插入到以UTF-8、UTF16或UTF-32编码Unicode文件开头的特殊标记。
这些标记对于 UTF-8 来说并不是必须的。所以,我们们可以将带有 BOM 的 UTF-8 转换为 UTF-8。
Unicode 字符集 BOM的对应关系

下面来看看字符编码与其bom的对应关系
字符编码 Bom (十六进制)
UTF-8EF BB BF
UTF-16 (BE) 大端FE FF
UTF-16 (LE) 小端FF FE
UTF-32 (BE) 大端00 00 FE FF
UTF-32 (LE) 小端FF FE 00 00
GB-1803084 31 95 33
UTF-8编码剖析


  • Unicode编码以code point来标识每一个字符, code point 的范围是
  • 0x000000 – 0x10FFFF
也就是每一个字符的code point都落在这个范围
而utf8的一个字符可以用1-4字节来表示,可能有人会说这code point最大也就是0x10FFFF,为什么最大不是可以用三个字节表示呢?那是因为utf8有自己独特的表示格式,先来看看下面的对应关系:
字节数 字符code point位数 最小的code point 最大的code point 第一个字节 第二个字节 第三个字节 第四个字节
17U+0000U+007F0XXXXXXX
211U+0080U+07FF110XXXXX10XXXXXX
316U+0800U+FFFF1110XXXX10XXXXXX10XXXXXX
421U+10000U+10FFFF11110XXX10XXXXXX10XXXXXX10XXXXXX


  • 当某个字符的code point (cp简称) U+0000 <= cp <= U+007F 落在这个范围内
这时只需要一个字节来表示 0XXXXXXX,将该字符的code point (7位)填入X的位置,就可以得到该字符的utf8的编码后的格式了。
我们以小写字母a举个例子,a的code point是01100001, 经过utf8编码后 01100001(0x61)


  • 例如,中文汉字 加 code point 为 0x52A0 二进制格式 ‭0101 0010 1010 0000
按照上表中的规则,该字符需要用3个字节来表示
按照填充规则 ,第一个字节 1110XXXX -> 11100101 , 第二个字节10XXXXXX -> 10001010 , 第三个字节10XXXXXX -> 10100000
组合起来就是 11100101 10001010 10100000 := ‭HEX-> 0xE58AA0‬
UTF-16编码剖析


  • utf-16编码的单元是2个字节,也就是16位。
utf-16编码格式在程序内存里经常使用,因为它比较高效,
java中Character 字符用的就是utf-16编码格式
在早期的时候,世界上所有的字符都可以用两个字节标识,也就是code point范围 U+0000 – U+FFFF,这样utf-16就可以很好的表示了,而且也不用像utf8那样按照固定的模板组合,可以直接用字符的code point表示,非常高效。
但是随着时间的推移,所有字符远远不能用两个字节的code point 表示了,那为了兼容code point 超过U+FFFF的字符 就出现字符代理对(Surrogate pair), utf16就是使用代理对来表示code point 范围在 U+10000 -> U+10FFFF之间的字符,当然也就的使用四个字节来表示该字符了。
对于Surrogate pair 与code point 之间的对应关系算法,等会儿再说。
先来看下utf16对于code point 小与U+10000的字符表示,其实用的就是字符的code point表示,这里还区分了大小端的表示法。


  • 案例
还是来看中文汉字 加 code point 为 0x52A0, 推测一下:
如果用utf16大端存储,那就是0x52A0;
如果用utf16小端存储,那就是0xA052
UTF-32编码剖析


  • utf-32用4个字节表示一个字符
  • 直接用字符的code point表示,非常高效,不需要任何的转化操作
  • 占用的存储空间却是很大的,会有空间的浪费。
  • 例如:小写字母a
code point 是 0x61
用utf32表示就是大端 -> 0x00 00 00 61 ; 小端 -> 0x61 00 00 00
这样会造成存储空间的浪费,当然应用场景不同而已,当追求高效的转换而忽略存储空间的浪费这个问题,utf32编码格式是比较好的选择。
而utf8的原则是尽可能的节省存储空间,牺牲转化的效率,各有各的好处。
判别Unicode文本的字符集的方法(Java) 【废弃/不可靠】

亲测,此方法并可绝对可靠(尤其是结果为 UTF-8 的情况)。
  1. /** * 获取 Unicode 文本的字符集 * @param textBytes * @return */ public static Charset getUnicodeTextCharset(byte[] textBytes){ String encoding = null; int bomSize = 4;//BOM_SIZE; byte bom[] = new byte[bomSize]; int n, unread; //n = internalIn.read(bom, 0, bom.length); //读取 bom int off = 0; int len = bom.length; int pos = 0; if (bom == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > bom.length - off) { throw new IndexOutOfBoundsException(); } int avail = bom.length <= textBytes.length ? bom.length : textBytes.length ;//算 bom.length 与 textBytes.length 的最小值 if (avail > 0) { System.arraycopy(textBytes, pos, bom, off, avail); } //判断 unicode 字符集 if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) { encoding = "UTF-32BE"; //unread = n - 4; } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) { encoding = "UTF-32LE"; //unread = n - 4; } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { encoding = "UTF-8";//utf08 with bom //unread = n - 3; } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { encoding = "UTF-16BE"; //unread = n - 2; } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { encoding = "UTF-16LE"; //unread = n - 2; } else { // Unicode BOM mark not found, unread all bytes //defaultEncoding = defaultEncoding == null ? Charset.defaultCharset().name() : defaultEncoding; //defaultEncoding = defaultEncoding == null ? null : defaultEncoding; //encoding = defaultEncoding; //unread = n; encoding = "UTF-8";//默认: UTF-8 (without bom) } // System.out.println("read=" + n + ", unread=" + unread); return Charset.forName(encoding); }
复制代码
最佳实践

UnicodeTextUtils : Unicode文本处理工具类

UnicodeCharsetEnum
  1. import com.xxx.sdk.pojo.text.enums.DigitalModeEnum; /** * Unicode 字符集 * @updateTime 2025.6.17 19:48 */ public enum UnicodeCharsetEnum { UTF8_WITH_BOM("UTF8_WITH_BOM", "UTF-8 With BOM", "UTF-8"), UTF8_WITHOUT_BOM("UTF8_WITHOUT_BOM", "UTF-8 Without BOM", "UTF-8"), //小端 UTF16LE_WITH_BOM("UTF16LE_WITH_BOM", "UTF-16LE With BOM", "UTF-16LE"), UTF16LE_WITHOUT_BOM("UTF16LE_WITHOUT_BOM", "UTF-16LE Without BOM", "UTF-16LE"), //大端 UTF16BE_WITH_BOM("UTF16BE_WITH_BOM", "UTF-16BE With BOM", "UTF-16BE"), UTF16BE_WITHOUT_BOM("UTF16BE_WITHOUT_BOM", "UTF-16BE Without BOM", "UTF-16BE"), //小端 UTF32LE_WITH_BOM("UTF32LE_WITH_BOM", "UTF-32LE With BOM", "UTF-32LE"), UTF32LE_WITHOUT_BOM("UTF32LE_WITHOUT_BOM", "UTF-32LE Without BOM", "UTF-32LE"), //大端 UTF32BE_WITH_BOM("UTF32BE_WITH_BOM", "UTF-32BE With BOM", "UTF-32BE"), UTF32BE_WITHOUT_BOM("UTF32BE_WITHOUT_BOM", "UTF-32BE Without BOM", "UTF-32BE"); private final String charsetCode; private final String charsetName; //java中定义的字符集 private final String javaCharset; public final static String CODE_PARAM = "code"; public final static String NAME_PARAM = "name"; UnicodeCharsetEnum(String charsetCode, String charsetName, String javaCharset) { this.charsetCode = charsetCode; this.charsetName = charsetName; this.javaCharset = javaCharset; } public static UnicodeCharsetEnum findByCharsetCode(String charsetCode) { for (UnicodeCharsetEnum type : values()) { if (type.getCharsetCode().equals(charsetCode)) { return type; } } return null; } public static UnicodeCharsetEnum findByCharsetName(String charsetName) { for (UnicodeCharsetEnum type : values()) { if (type.getCharsetName().equals(charsetName)) { return type; } } return null; } public String getCharsetName() { return charsetName; } public String getCharsetCode() { return charsetCode; } public String getJavaCharset() { return javaCharset; } }
复制代码
UnicodeTextUtils
  1. import com.xxx.sdk.pojo.text.UnicodeCharsetEnum; import java.io.UnsupportedEncodingException; /** * Unicode 文本处理工具类 * @updateTime 2025.6.17 19:47 */ public class UnicodeTextUtils { /** * 将指定文本转换为指定 Unicode 字符集的字节数组 * @param text Java 字符串 * eg: "hello world!你好!" * @param unicodeCharset * eg: UTF8_WITH_BOM * @return 指定 Unicode 字符集的字节数组 * @usage String newText = new String( textToBytes(text="hello world!你好!", UTF8_WITH_BOM) , UTF8_WITH_BOM.charset) */ public static byte [] textToBytes(String text, UnicodeCharsetEnum unicodeCharset) throws UnsupportedEncodingException { byte [] textBytes = null; switch (unicodeCharset) { // UTF8 不涉及 字节序(大小端)问题 (每个文本字符的最小单元: 1 byte) case UTF8_WITH_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-8" int bomLength = 3; byte [] textBytes2 = new byte [textBytes1.length + bomLength];//预留 3个字节,填充 bom System.arraycopy(textBytes1, 00, textBytes2, 0 + bomLength, textBytes1.length); textBytes2[0] = (byte)0xef; textBytes2[1] = (byte)0xbb; textBytes2[2] = (byte)0xbf; //text == newText == "hello world!你好!", newText == [ (byte)0xef, (byte)0xbb, (byte)0xbf, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, -28, -67, -96, -27, -91, -67, 33 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes2; break; } case UTF8_WITHOUT_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-8" //text == newText == "hello world!你好!", newText == [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, -28, -67, -96, -27, -91, -67, 33 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes1; break; } //UTF16 设计 字节序(大小端)问题 (每个文本字符的最小单元: 2 byte) case UTF16LE_WITH_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-16LE" int bomLength = 2; byte [] textBytes2 = new byte [textBytes1.length + bomLength];//预留 2个字节,填充 bom System.arraycopy(textBytes1, 00, textBytes2, 0 + bomLength, textBytes1.length); textBytes2[0] = (byte)0xff; textBytes2[1] = (byte)0xfe; //text == newText == "hello world!你好!", newText == [ 0xff/-1, 0xfe/-2, 104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33, 0, 96, 79, 125, 89, 33, 0 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes2; break; } case UTF16LE_WITHOUT_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-16LE" //text == newText == "hello world!你好!", newText == [ 104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33, 0, 96, 79, 125, 89, 33, 0 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes1; break; } case UTF16BE_WITH_BOM : { //方法1 byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-16BE" int bomLength = 2; byte [] textBytes2 = new byte [textBytes1.length + bomLength];//预留 2个字节,填充 bom System.arraycopy(textBytes1, 00, textBytes2, 0 + bomLength, textBytes1.length); textBytes2[0] = (byte)0xfe; textBytes2[1] = (byte)0xff; //方法2 //byte [] textBytes2 = (new String( text )).getBytes( "UTF-16" );//仅适用于 utf16 BE with bom(0xfe = -2, 0xff=-1) //text == newText == "hello world!你好!", newText == [ 0xfe/-2, 0xff/-1, 0, 104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33, 79, 96, 89, 125, 0, 33 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes2; break; } case UTF16BE_WITHOUT_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-16BE" //text == newText == "hello world!你好!", newText == [ 0, 104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33, 79, 96, 89, 125, 0, 33 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes1; break; } //UTF32 设计 字节序(大小端)问题 (每个文本字符的最小单元: 4 byte) case UTF32LE_WITH_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-32LE" int bomLength = 4; byte [] textBytes2 = new byte [textBytes1.length + bomLength];//预留 4个字节,填充 bom System.arraycopy(textBytes1, 00, textBytes2, 0 + bomLength, textBytes1.length); textBytes2[0] = (byte)0xff; textBytes2[1] = (byte)0xfe; textBytes2[2] = (byte)0x00; textBytes2[3] = (byte)0x00; //text == newText == "hello world!你好!", newText == //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes2; break; } case UTF32LE_WITHOUT_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-32LE" //text == newText == "hello world!你好!", newText == //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes1; break; } case UTF32BE_WITH_BOM : { //方法1 byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-32BE" int bomLength = 4; byte [] textBytes2 = new byte [textBytes1.length + bomLength];//预留 2个字节,填充 bom System.arraycopy(textBytes1, 00, textBytes2, 0 + bomLength, textBytes1.length); textBytes2[0] = (byte)0x00; textBytes2[1] = (byte)0x00; textBytes2[2] = (byte)0xfe; textBytes2[3] = (byte)0xff; //text == newText == "hello world!你好!", newText == //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes2; break; } case UTF32BE_WITHOUT_BOM : { byte [] textBytes1 = (new String( text )).getBytes( unicodeCharset.getJavaCharset() );//"UTF-32BE" //方法2 //byte [] textBytes2 = (new String( text )).getBytes( "UTF-32" );//仅适用于 utf32 BE without bom(0x00, 0x00, 0xfe = -2, 0xff=-1) //text == newText == "hello world!你好!", newText == [ 0, 0, 0, 104, 0, 0, 0, 101, 0, 0, 0, 108, 0, 0, 0, 108, 0, 0, 0, 111, 0, 0, 0, 32, 0, 0, 0, 119, 0, 0, 0, 111, 0, 0, 0, 114, 0, 0, 0, 108, 0, 0, 0, 100, 0, 0, 0, 33, 0, 0, 79, 96, 0, 0, 89, 125, 0, 0, 0, 33 ] //String newText = new String( textBytes2, unicodeCharset.getJavaCharset() ); textBytes = textBytes1; break; } default: { //do nothing break; } } return textBytes; } }
复制代码
UnicodeTextUtilsTest
  1. package com.xxx.sdk.utils.text; import com.xxx.sdk.pojo.text.UnicodeCharsetEnum; import com.xxx.sdk.utils.bytes.BytesUtils; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @Slf4j public class UnicodeTextUtilsTest { @Test public void textToBytesTest() throws Exception { String text = "hello world!你好!"; //efbbbf68656c6c6f20776f726c6421e4bda0e5a5bd21 log.info( "UTF8_WITH_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF8_WITH_BOM) )); //68656c6c6f20776f726c6421e4bda0e5a5bd21 log.info( "UTF8_WITHOUT_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF8_WITHOUT_BOM) )); //fffe680065006c006c006f00200077006f0072006c0064002100604f7d592100 log.info( "UTF16LE_WITH_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF16LE_WITH_BOM) )); //680065006c006c006f00200077006f0072006c0064002100604f7d592100 log.info( "UTF16LE_WITHOUT_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF16LE_WITHOUT_BOM) )); //feff00680065006c006c006f00200077006f0072006c006400214f60597d0021 log.info( "UTF16BE_WITH_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF16BE_WITH_BOM) )); //00680065006c006c006f00200077006f0072006c006400214f60597d0021 log.info( "UTF16BE_WITHOUT_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF16BE_WITHOUT_BOM) )); //fffe000068000000650000006c0000006c0000006f00000020000000770000006f000000720000006c0000006400000021000000604f00007d59000021000000 log.info( "UTF32LE_WITH_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF32LE_WITH_BOM) )); //68000000650000006c0000006c0000006f00000020000000770000006f000000720000006c0000006400000021000000604f00007d59000021000000 log.info( "UTF32LE_WITHOUT_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF32LE_WITHOUT_BOM) )); //0000feff00000068000000650000006c0000006c0000006f00000020000000770000006f000000720000006c000000640000002100004f600000597d00000021 log.info( "UTF32BE_WITH_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF32BE_WITH_BOM) )); //00000068000000650000006c0000006c0000006f00000020000000770000006f000000720000006c000000640000002100004f600000597d00000021 log.info( "UTF32BE_WITHOUT_BOM:{}", BytesUtils.bytesToHexString( UnicodeTextUtils.textToBytes(text, UnicodeCharsetEnum.UTF32BE_WITHOUT_BOM) )); } }
复制代码
Y 推荐文献


  • [网络传输/序列化/CPU/内存/计算机组成原理] 字节序/大小端 - 博客园/千千寰宇
  • [Java SE] 基础工具类:ByteUtils(字节操作) - 博客园/千千寰宇
  • java - 如何使用 BOM 编码/解码 UTF-16LE 字节数组? - StackoverFlow
用 BOM 将 a 编码 java.lang.String 为 UTF-16 little endian 的方法
  1. public static byte[] encodeString(String message) { byte[] tmp = null; try { tmp = message.getBytes("UTF-16LE"); } catch(UnsupportedEncodingException e) { // should not possible AssertionError ae = new AssertionError("Could not encode UTF-16LE"); ae.initCause(e); throw ae; } // use brute force method to add BOM byte[] utf16lemessage = new byte[2 + tmp.length]; utf16lemessage[0] = (byte)0xFF; utf16lemessage[1] = (byte)0xFE; System.arraycopy(tmp, 0, utf16lemessage, 2, tmp.length); return utf16lemessage; }
复制代码
这是一个老问题,但我仍然找不到适合我情况的可接受答案。基本上,Java 没有内置的带有 BOM 的 UTF-16LE 编码器。因此,您必须推出自己的实现。 2017-08-24T22:17:10.220
  1. private byte[] encodeUTF16LEWithBOM(final String s) { ByteBuffer content = Charset.forName("UTF-16LE").encode(s); byte[] bom = { (byte) 0xff, (byte) 0xfe }; return ByteBuffer.allocate(content.capacity() + bom.length).put(bom).put(content).array(); }
复制代码
X 参考文献


  • 字符集编码 Unicode UTF8 UTF16 UTF32 和BOM(Byte Order Mark) - CSDN
  • win10下怎么把txt格式转换为无bom的utf-8形式? - Zhihu
  • UTF-8、UTF-16、UTF-32 还区分带 BOM 的以及不带 BOM 的 Unicode 文本。
本文作者: 千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册