You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 12:37:01