Kotlin写入中文到文本文件:BeanIO固定长度字段对齐问题
Kotlin+BeanIO固定长度文件中文对齐问题解决方案
问题背景
使用Kotlin结合BeanIO生成固定长度文本文件时,transactionDescription字段配置长度为25。英文内容「Buyers cash1」与中文内容「今天去雜貨店購物100元」字符数均为12,但UTF-8编码下中文每个字符占2字节,导致写入文件后实际占用空间超出预期,后续字段错位。需要让12个中文字符仅补1个空格即可适配25的字段长度,保证内容对齐。
核心思路
BeanIO默认按字符数处理固定长度字段,但中文/全角字符在显示和字节占用上等价于2个半角字符。因此需要自定义逻辑,按字符显示宽度(中文/全角算2宽度,英文/半角算1宽度)来计算和调整字段内容,确保总宽度匹配BeanIO配置的25。
解决方案步骤
1. 实现字符宽度计算工具函数
添加工具函数,计算字符串的实际显示宽度:
fun calculateDisplayWidth(str: String): Int { var width = 0 for (c in str.toCharArray()) { // 匹配中文、全角字符的Unicode范围,可根据需求扩展 val unicode = c.toInt() width += if (unicode in 0x4E00..0x9FAF || unicode in 0xFF00..0xFFEF) { 2 } else { 1 } } return width }
2. 修改transactionDescription字段处理逻辑
在generatePostingOutputFile方法中,替换原有的字符数处理逻辑,改为按显示宽度调整内容:
override fun generatePostingOutputFile( postingHeader: PostingHeader, postingBeanList: List<PostingBean>, postingTrailer: PostingTrailer, paramDate: String ) { val factory = StreamFactory.newInstance() try { factory.load(PostingServiceImpl::class.java.getResourceAsStream("/models/PostingDataGenerationByTc.xml")) var path = postingOutputFileDirectory + postingOutputFileName + "_" + paramDate + postingOutputFileExtension if (path.startsWith("classpath:")) { val url = ClassLoader.getSystemResource("") path = url.path + path.substring("classpath:".length) } // 目标宽度为BeanIO配置的25 val targetWidth = 25 postingBeanList.forEach { postingBean -> val originalDescription = postingBean.transactionDescription ?: "" val currentWidth = calculateDisplayWidth(originalDescription) var processedDesc = originalDescription when { currentWidth > targetWidth -> { // 截断内容至目标宽度内 val sb = StringBuilder() var currentCount = 0 for (c in originalDescription.toCharArray()) { val charWidth = if (c.toInt() in 0x4E00..0x9FAF || c.toInt() in 0xFF00..0xFFEF) 2 else 1 if (currentCount + charWidth > targetWidth) break sb.append(c) currentCount += charWidth } processedDesc = sb.toString() } currentWidth < targetWidth -> { // 补充半角空格至目标宽度 val needSpaces = targetWidth - currentWidth processedDesc = originalDescription + StringUtils.repeat(" ", needSpaces) } } postingBean.transactionDescription = processedDesc } val file = File(path) Files.createParentDirs(file) val encoding = StandardCharsets.UTF_8 val outputStream = FileOutputStream(file) val writer = OutputStreamWriter(outputStream, encoding) val out = factory.createWriter("posting", writer) out.write(postingHeader) postingBeanList.forEach(out::write) out.write(postingTrailer) out.flush() out.close() } catch (e: IOException) { log.error("Printing stack trace: ", e) log.info(e.message) } }
3. 修正PostingBean中的函数调用错误
原代码中init块调用的toFullWidth与函数定义convertToFullWidth名称不匹配,修正为:
init { idN= tcPostingDataView.idN Type = tcPostingDataView.Type uniKey = tcPostingDataView.uniKey Code = tcPostingDataView.Code // 修正函数名 transactionDescription = convertToFullWidth(tcPostingDataView.transactionDescription) Amount = StringUtils.repeat("0", 11 - tcPostingDataView.amount?.movePointRight(2).toString().length) + tcPostingDataView.amount?.movePointRight(2).toString() }
4. 保持BeanIO配置不变
原配置中transactionDescription的length设为25无需修改,确保与代码中的目标宽度一致:
<field name="transactionDescription" length="25"/>
效果验证
- 12个中文字符(总宽度24)会自动补充1个半角空格,总宽度达到25,与配置匹配。
- 英文/半角字符按1宽度计算,12个字符补充13个空格即可达到25宽度,保证后续字段对齐。
内容的提问来源于stack exchange,提问作者yameez
相关产品推荐
相关产品推荐

