如何临时忽略java.nio.charset.MalformedInputException并替换错误字符?
解决YAML自动识别字符集时的MalformedInputException问题
问题分析
当尝试用默认字符集(如UTF-8)加载其他编码(如GBK)的YAML文件时,若文件包含无法解码的字符,SnakeYAML会抛出MalformedInputException,导致无法读取文件中的metadata.charset字段,进而无法完成自动字符集切换。
解决方案
核心思路是在第一次临时加载YAML获取字符集配置时,使用容错字符解码器,将无法解码的错误字符替换为占位符,避免抛出异常。后续用正确字符集重新加载时,仍使用严格解码保证数据正确性。
步骤1:创建容错字符解码器
构建一个宽松模式的CharsetDecoder,指定错误处理策略为替换:
private CharsetDecoder createFaultTolerantDecoder(Charset baseCharset) { return baseCharset.newDecoder() // 处理格式错误的输入时替换 .onMalformedInput(CodingErrorAction.REPLACE) // 处理无法映射的字符时替换 .onUnmappableCharacter(CodingErrorAction.REPLACE) // 指定替换字符,可改为"?"或空串等 .replaceWith("�"); }
步骤2:修改临时加载YAML的代码
替换原直接用yaml.load(inputStream)的逻辑,改用容错解码器创建StreamReader:
if (Objects.isNull(charset)) { if (getInputFile().exists()) { final InputStream inputStream = getInputStream(); try { // 使用默认字符集创建容错解码器 CharsetDecoder tolerantDecoder = createFaultTolerantDecoder(Charset.defaultCharset()); // 基于容错解码器创建StreamReader StreamReader streamReader = new StreamReader(new InputStreamReader(inputStream, tolerantDecoder)); // 加载YAML文件(此时不会因编码错误抛出异常) final Object object = yaml.load(streamReader); if (object instanceof Map) { @SuppressWarnings("unchecked") final String charsetName = Structure.of((Map<String, Object>) object).getStringOrFail(charsetPath); charset = Charset.forName(charsetName); } else { throw new IOException("yaml value is an instance of type '" + object.getClass().getName() + "', not required 'Map<String, Object>'!"); } } catch (Throwable throwable) { throw new IOException("can not find the charset in auto charset yaml file: " + getInputFile(), throwable); } finally { // 记得关闭输入流 inputStream.close(); } } else { charset = defaultCharset; } }
关键说明
- 仅临时生效:该容错逻辑只用于第一次加载YAML获取
metadata.charset字段,后续用正确字符集重新加载时,使用默认的严格解码规则,不会影响其他代码的异常抛出行为。 - 替换字符不影响配置读取:只要
metadata.charset字段的字符在默认字符集中可正确解码(或错误字符替换后不影响字符串解析),就能顺利获取到正确的字符集配置。 - 资源释放:添加了输入流的关闭逻辑,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Chuanwise
相关产品推荐
相关产品推荐

