writeUTF输出字符串失败的原因分析

本文探讨了Java中使用DataOutput接口写入UTF字符串时的长度限制问题,当字符串编码后的长度超过65535字节时将引发异常,并建议实际应用中的字符串长度不要超过20000字符。
字符串比较长了之后,数据就发不过去了,经检查JDK的源代码,原来有长度限制。
为了保险起见,我们还是不要超过65535/3 我看取20000好了。

  1.     public final void writeUTF(String str) throws IOException {
  2.         writeUTF(str, this);
  3.     }
  4.   static int writeUTF(String str, DataOutput out) throws IOException {
  5.     int strlen = str.length();
  6.     int utflen = 0;
  7.     int c, count = 0;
  8.     /* use charAt instead of copying String to char array */
  9.     for (int i = 0; i < strlen; i++) {
  10.       c = str.charAt(i);
  11.       if ((c >= 0x0001) && (c <= 0x007F)) {
  12.         utflen++;
  13.       } else if (c > 0x07FF) {
  14.         utflen += 3;
  15.       } else {
  16.         utflen += 2;
  17.       }
  18.     }
  19.     if (utflen > 65535)
  20.       throw new UTFDataFormatException("encoded string too long: " + utflen + " bytes");
  21.     // 其他的语句
  22.   }











<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<think>我们被要求回答关于DataOutputStream.writeUTF方法是否自动写入字符串长度的问题,以及如果不自动写入时如何先写入字符串长度。 根据引用[2]和引用[3]: 引用[2]明确指出:writeUTF字符串按照UTF编码后的字节长度写入目标设备,然后才是每一个字节的UTF编码。 引用[3]也说明:writeUTF()方法用于将给定的字符串值写入基本数据输出流,使用UTF-8编码方案,并且会写入字符串的长度。 因此,DataOutputStream.writeUTF方法在写入字符串时,会自动先写入两个字节(无符号short)表示后续UTF-8编码的字节数,然后再写入UTF-8编码的字节。 但是,注意引用[4]提到的问题:writeUTF方法在写入字符串时,要求字符串的UTF-8编码长度不能超过65535(即两个字节表示的最大无符号整数),否则会抛出UTFDataFormatException。因此,对于长字符串,我们需要自己处理。 所以,对于用户的问题: 1. writeUTF会自动写入字符串长度(以两个字节的无符号short形式写入,表示后续UTF-8编码的字节数)。 2. 如果我们想要自己先写入字符串的长度(比如我们自己定义的长度表示方式),那么就不能直接使用writeUTF,而需要自己拆分字符串并写入。 引用[4]给出了一个处理长字符串的例子:先写入字符串的长度(注意:这里写入的是字符串的字符长度,而不是字节长度),然后将长字符串拆分成多个小于等于65535的片段,每个片段用writeUTF写入。但请注意,这种方法写入的长度是字符长度,而每个片段写入的是UTF-8编码的字符串(每个片段都会写入该片段UTF-8编码的字节长度)。 因此,如果我们想要自己控制字符串长度的写入(例如,我们想用4个字节表示长度,或者想避免每个片段都写入长度),我们可以这样做: - 将字符串转换为UTF-8编码的字节数组。 - 先写入这个字节数组的长度(可以用writeInt写入4个字节,或者用其他方式)。 - 然后写入整个字节数组(使用write(byte[]))。 这样,读取的时候先读取长度,再读取对应长度的字节,然后转换成字符串。 总结: 1. writeUTF自动写入字符串的UTF-8编码长度(两个字节),然后写入UTF-8字节。 2. 如果不希望使用自动写入的长度(例如要处理超长字符串,或者想要用不同的长度表示方式),则应该自己将字符串转换为字节数组,然后先写入数组长度(自定义格式),再写入字节数组。 下面给出两种方式的代码示例: 方式一:使用writeUTF(自动写入长度,适用于字符串的UTF-8编码长度不超过65535的情况) ```java DataOutputStream dos = ...; String str = "Hello World"; dos.writeUTF(str); // 自动写入两个字节的长度和UTF-8编码的字符串 ``` 方式二:手动处理(适用于长字符串或自定义长度格式) ```java DataOutputStream dos = ...; String str = "超长字符串..."; byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8); // 将字符串转换为UTF-8字节数组 // 先写入字节数组的长度(使用4个字节的int) dos.writeInt(utf8Bytes.length); // 再写入字节数组 dos.write(utf8Bytes); ``` 读取时: 方式一:使用DataInputStream的readUTF 方式二:先读取int长度,然后读取该长度的字节,再转换为字符串。 注意:方式二没有使用writeUTF,所以读取时也不能用readUTF。 根据引用[4]的提示,如果字符串非常长,我们也可以将字符串分块写入,但每块的大小要控制在65535个UTF-8编码字节以内。引用[4]的示例代码是先写入字符串的字符长度(注意不是字节长度),然后分块写入(每块用writeUTF)。但这种方法在读取时也需要相应地读取:先读取总长度,然后分块读取。 因此,我们根据实际需求选择。 最后,回答问题: writeUTF会自动写入字符串的UTF-8编码长度(两个字节),所以不需要我们手动先写入长度。但如果需要处理超长字符串(UTF-8编码超过65535字节),则必须手动处理,此时需要先写入长度(自定义方式)再写入字节。 相关问题: §§相关问题§§ 1. 如何读取由DataOutputStream.writeUTF写入的字符串? 2. 处理长字符串时,除了分块还有什么方法? 3. Java中字符串编码转换的注意事项? 4. 如何避免字符串转换为字节数组时的内存溢出? </think> ### DataOutputStream.writeUTF 方法是否自动写入字符串长度? #### 1. **writeUTF 的自动长度写入机制** `DataOutputStream.writeUTF()` 方法在写入字符串时**会自动处理字符串长度**,具体机制如下: ```java DataOutputStream dos = new DataOutputStream(outputStream); dos.writeUTF("示例字符串"); ``` - **自动写入长度**:该方法首先写入一个**2字节的无符号短整型**(`char`),表示后续UTF-8编码字节的长度[^2][^3]。 - **数据格式**: $$ \text{输出结构} = \underbrace{2\ \text{字节}}_{\text{长度}} + \underbrace{\text{UTF-8编码字节}}_{\text{字符串数据}} $$ - **示例**: 写入 `"A"`(1字符): - 长度字段:`0x0001`(2字节) - 数据字段:`0x41`(UTF-8编码) #### 2. **手动写入长度的场景与实现** 当需要**自定义长度字段**(如处理超长字符串或特殊协议)时,需绕过 `writeUTF`,手动控制: ```java // 自定义写入:先手动写长度,再写UTF-8字节 byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8); // 写入4字节长度(int类型,支持更长字符串dos.writeInt(utf8Bytes.length); // 手动写长度 dos.write(utf8Bytes); // 写原始字节 ``` ##### 对比两种方式: | 特性 | `writeUTF()` | 手动写入 | |------|--------------|----------| | 长度字段 | 自动写入(2字节) | 需手动写入 | | 长度类型 | `char`(最大65535) | 可自定义(如`int`) | | 适用场景 | 常规字符串(≤65535字节) | 超长字符串/自定义协议 | #### 3. **超长字符串处理方案** 当字符串UTF-8编码超过65535字节时,`writeUTF()` 会抛出 `UTFDataFormatException`[^4]。解决方案: ```java // 分块写入(每块≤65535字节) public static void writeLongString(DataOutputStream dos, String str) throws IOException { byte[] allBytes = str.getBytes(StandardCharsets.UTF_8); int maxChunk = 65535; // 最大单块字节数 // 写入总字节数(4字节) dos.writeInt(allBytes.length); // 分块写入 for (int offset = 0; offset < allBytes.length; offset += maxChunk) { int chunkSize = Math.min(maxChunk, allBytes.length - offset); dos.write(allBytes, offset, chunkSize); } } ``` #### 4. **读取时的对应操作** - **自动长度模式**:用 `DataInputStream.readUTF()` 读取。 - **手动长度模式**:先读长度,再读字节并转换: ```java int length = dis.readInt(); // 读取手动写入的长度 byte[] bytes = new byte[length]; dis.readFully(bytes); String str = new String(bytes, StandardCharsets.UTF_8); ``` > ⚠ **关键点** > - `writeUTF()` 的长度字段是**UTF-8编码字节数**,非字符数(如中文一个字符占3字节)[^2]。 > - 手动写入时需显式指定字符集(如 `StandardCharsets.UTF_8`),避免平台默认编码问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值