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

Freemarker打印日文字符时补位对齐异常问题求助

问题原因与解决方案

这个问题的核心在于日文这类宽字符的显示宽度和单字节英文不一致,而Freemarker的right_pad方法默认是按字符数量来计算填充长度的,不是按实际显示宽度。

为什么会不对齐?

  • 日文属于双字节宽字符,在终端或打印输出中,1个日文字符会占用2个英文字符的显示宽度;而英文、破折号、空格这类单字节字符只占1个宽度。
  • 第一行里,"こんにちは世界"是6个日文字符,用right_pad(12, "-")会填充6个破折号,实际显示宽度是 6*2 + 6*1 = 18;而"こんにちは"是5个日文字符,填充7个破折号,实际显示宽度是 5*2 +7*1=17——两者宽度差1,自然无法对齐。
  • 第二行用默认空格填充,同样是按字符数填充到12,但两个字符串的实际显示宽度依然不同,导致后面的*位置错开。
  • 你添加的<#ftl encoding="Shift_JIS">是用来指定模板文件的编码,确保模板内容读取时不会乱码,和字符显示宽度的计算完全无关,所以解决不了对齐问题。

解决方法:按显示宽度计算填充量

我们需要先计算字符串的实际显示宽度,再根据目标显示宽度来计算需要填充的字符数,而不是依赖Freemarker默认的按字符数填充。

  1. 第一步:编写Java工具方法计算显示宽度
    先写一个方法,区分宽字符和单字节字符,计算字符串的实际显示宽度:

    public static int getDisplayWidth(String str) {
        int displayWidth = 0;
        for (char c : str.toCharArray()) {
            // 判断是否为日文、中文等宽字符
            Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
            if (block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                    || block == Character.UnicodeBlock.HIRAGANA
                    || block == Character.UnicodeBlock.KATAKANA) {
                displayWidth += 2;
            } else {
                displayWidth += 1;
            }
        }
        return displayWidth;
    }
    
  2. 第二步:将方法暴露给Freemarker模板
    在你的Java代码中,把这个工具方法注册为Freemarker的全局方法,比如:

    Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
    // 其他配置...
    cfg.setSharedVariable("displayWidth", new TemplateMethodModelEx() {
        @Override
        public Object exec(List arguments) throws TemplateModelException {
            if (arguments.isEmpty()) {
                throw new TemplateModelException("需要传入字符串参数");
            }
            String str = ((SimpleScalar) arguments.get(0)).getAsString();
            return getDisplayWidth(str);
        }
    });
    
  3. 第三步:在模板中按显示宽度填充
    现在可以在FTL里用这个方法计算需要填充的字符数,确保最终显示宽度一致:

    <#-- 目标显示宽度,比如要对应12个单字符的宽度,这里设为24 -->
    <#assign targetDisplayWidth = 24>
    
    <#assign str1 = "こんにちは世界">
    <#assign padCount1 = targetDisplayWidth - displayWidth(str1)>
    <#assign paddedStr1 = str1 + "-"?repeat(padCount1)>
    
    <#assign str2 = "こんにちは">
    <#assign padCount2 = targetDisplayWidth - displayWidth(str2)>
    <#assign paddedStr2 = str2 + "-"?repeat(padCount2)>
    
    output starts ${paddedStr1} ${paddedStr2} output ends
    

    这样处理后,两个字符串的实际显示宽度都会是24,就能完美对齐了。

内容的提问来源于stack exchange,提问作者Banupriya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:02:29