当前位置:首页 > 文章列表 > 文章 > java教程 > Java FieldPosition 怎样定位数字格式化后的整数部分

Java FieldPosition 怎样定位数字格式化后的整数部分

来源:17golang原创 2026-11-10 12:26:38 0浏览 收藏

Java 里如果只是把数字转成字符串,直接调用 NumberFormat.format() 就够了;但如果还要把格式化结果中的整数部分单独高亮、对齐或交给下一步绘制,就不应该靠查找小数点来猜范围。更稳妥的做法是创建 FieldPosition,在调用带 StringBuffer 和位置参数的 format 重载时请求 NumberFormat.INTEGER_FIELD。

核心写法是:让 NumberFormat 生成结果,同时让 FieldPosition 记录整数域的 beginIndex 和 endIndex。随后用 result.substring(begin, end) 取出整数部分,注意 endIndex 是结束位置的后一位。

官方文档:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/text/FieldPosition.html

先把问题拆成格式化和定位两件事

假设小项目要把订单金额格式化为带分组分隔符的文本,并把整数部分交给 UI 做单独样式。输入 1234567.89 经过美国 Locale 的数字格式化后可能变成 1,234,567.89。这时整数部分包含逗号,负数还可能带负号或货币前缀,固定写成 substring(0, 9) 很快就会失效。

FieldPosition 不是用来修改字符串的,它只是由格式器填入一个字段的位置。官方 API 把它描述为记录格式化输出中字段起点和终点的简单对象;getEndIndex() 返回的是“最后一个字符之后”的位置。

数字经过 NumberFormat 与 INTEGER_FIELD 后生成带整数范围标记的格式化结果说明图
图1:FieldPosition 定位流程说明图,展示格式化文本与整数域位置如何同时产生。

创建 NumberFormat 和整数域定位器

先用 NumberFormat.getNumberInstance(Locale.US) 得到一个按指定 Locale 工作的格式器,再创建 new FieldPosition(NumberFormat.INTEGER_FIELD)。这里传入的是整数域常量,不是结果字符串中的下标。

import java.text.FieldPosition;
import java.text.NumberFormat;
import java.util.Locale;

public class IntegerFieldDemo {
    public static void main(String[] args) {
        // 固定 Locale,避免机器默认地区改变分组符号和小数符号。
        NumberFormat formatter = NumberFormat.getNumberInstance(Locale.US);

        // 请求格式器记录整数部分在最终文本中的范围。
        FieldPosition integerField = new FieldPosition(NumberFormat.INTEGER_FIELD);

        System.out.println(integerField.getField());
    }
}

如果业务本来就要跟随用户地区,可以把 Locale.US 换成用户选择的 Locale,但要把 Locale 当成格式化输入的一部分管理。不要在测试里依赖开发机默认 Locale,否则同一组数据的逗号和小数点可能改变。

用带位置参数的 format 取得范围

真正填充位置的是下面这个重载:format(Object, StringBuffer, FieldPosition)。调用结束后,StringBuffer 里是完整文本,FieldPosition 里是整数域的起止索引。

import java.text.FieldPosition;
import java.text.NumberFormat;
import java.util.Locale;

public class IntegerFieldDemo {
    public static void main(String[] args) {
        // 使用固定地区,让示例输出稳定且包含千位分隔符。
        NumberFormat formatter = NumberFormat.getNumberInstance(Locale.US);
        FieldPosition integerField = new FieldPosition(NumberFormat.INTEGER_FIELD);
        StringBuffer buffer = new StringBuffer();

        // 格式器同时写入文本,并把整数域位置写入 integerField。
        formatter.format(1234567.89, buffer, integerField);

        String formatted = buffer.toString();
        int begin = integerField.getBeginIndex();
        int end = integerField.getEndIndex();

        // end 是开区间右边界,因此可直接用于 substring。
        String integerPart = formatted.substring(begin, end);
        System.out.println("完整结果: " + formatted);
        System.out.println("整数部分: " + integerPart);
        System.out.println("范围: [" + begin + ", " + end + ")");
    }
}

对于这个输入,输出的关键关系是:

完整结果: 1,234,567.89
整数部分: 1,234,567
范围: [0, 9)

这里的 9 不代表整数部分有九个数字,而是代表从索引 0 开始,到小数点前一位之后结束。逗号也属于格式化文本中的整数域范围,所以截取结果会保留逗号。

读取 beginIndex 和 endIndex 时不要写死下标

beginIndex 是整数域第一个字符的位置,endIndex 是整数域最后一个字符之后的位置。两者组成左闭右开区间,正好可以交给 substring。

可以把它想成一把落在结果字符串上的尺子:起点指向整数部分的第一个字符,终点落在小数点前面。对于 1,234,567.89,整数域是 [0, 9);对于带前缀、负数或不同数字系统的结果,起点和终点都应该以本次格式化后读取到的值为准。

1,234,567.89 字符格上用 beginIndex 和 endIndex 标出整数部分边界的说明图
图2:beginIndex 与 endIndex 结果示意图,强调结束索引位于整数部分之后。

如果只是想把整数部分包进 HTML 或富文本标记,也应先取得这两个索引,再拼接前缀、整数片段和后缀。不要在格式化之后重新扫描逗号或小数点,因为 Locale 可能使用不同的分隔符。

分组符、负数和 Locale 会怎样影响结果

FieldPosition 定位的是格式化后的字符范围,不是原始数字的位数。下面几个边界值得提前处理:

  • 分组符:开启分组后,逗号或其他分组字符会出现在整数域文本中,截取整数部分时会被保留。
  • 负数:负号可能位于整数域之外或由具体格式器以不同方式呈现,不要假设整数片段一定从索引 0 开始。
  • Locale:小数符号、分组符号和数字字符可能变化,索引必须来自同一次 format 调用。
  • 货币格式:货币符号、空格或括号可能在数字前后出现;如果要定位货币符号、整数和小数等多个属性,单个整数 FieldPosition 可能不够。
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.util.Locale;

public class LocaleBoundaryDemo {
    public static void main(String[] args) {
        // 让格式器按德国地区输出,示例中的分组符和小数符会与 US 不同。
        NumberFormat formatter = NumberFormat.getNumberInstance(Locale.GERMANY);
        FieldPosition integerField = new FieldPosition(NumberFormat.INTEGER_FIELD);
        StringBuffer buffer = new StringBuffer();

        // 位置只能解释当前这一次格式化结果,不能跨 Locale 复用。
        formatter.format(-1234567.89, buffer, integerField);

        String text = buffer.toString();
        System.out.println("格式化结果: " + text);
        System.out.println("整数域: " + text.substring(
                integerField.getBeginIndex(), integerField.getEndIndex()));
    }
}

这个示例的重点不是某个地区最终显示的标点,而是调用顺序:先创建与本次格式化匹配的 FieldPosition,再读取这次结果对应的索引。

需要全部字段时改用 formatToCharacterIterator

FieldPosition 适合只关心一个字段,例如整数部分。如果要同时取得整数、小数、货币符号等多个属性,可以考虑 Format.formatToCharacterIterator()。它返回带属性范围的 AttributedCharacterIterator,用途比单个 FieldPosition 更宽。

import java.text.AttributedCharacterIterator;
import java.text.NumberFormat;
import java.util.Locale;

public class AttributeRangeDemo {
    public static void main(String[] args) {
        // 采用 Locale 生成带属性的格式化文本。
        NumberFormat formatter = NumberFormat.getNumberInstance(Locale.US);
        AttributedCharacterIterator iterator = formatter.formatToCharacterIterator(1234567.89);

        // 遍历属性边界,便于同时处理整数、分数等多个字段。
        int index = iterator.getBeginIndex();
        while (index  [" + index + ", " + runLimit + ")");
            index = runLimit;
        }
    }
}

选择标准很简单:只要一个字段的位置,就用 FieldPosition;需要理解格式化结果里多个字段的属性范围,就用字符迭代器。两者都应以实际格式化结果为准,不要把某个 Locale 下的固定位置当成通用协议。

常见问题

为什么直接调用 format(String) 后 FieldPosition 没有位置?

只返回字符串的便捷重载不会把你创建的 FieldPosition 传进去。需要使用接收 StringBuffer 和 FieldPosition 的重载,让格式器在这次调用中填充位置。

endIndex 要不要减一?

不要。它是字段末尾之后的位置,正好是 substring(beginIndex, endIndex) 所需的右边界;只有在访问“最后一个字符”时才需要使用 endIndex - 1,并且要先确认范围非空。

能不能用小数点位置代替 FieldPosition?

不建议。小数点可能因 Locale 改变,整数还可能带分组符、负号、货币前缀或后缀。FieldPosition 直接由格式器提供字段范围,意图更清晰。

FieldPosition 可以在多个线程之间共享吗?

不要把一次格式化过程中的 FieldPosition 当成共享状态。它会被本次调用写入,实际使用时应和本次结果一起创建和消费;同时,NumberFormat 及其实现通常也不是同步的,并发场景应按线程隔离或外部同步。

总结

Java FieldPosition 定位数字格式化后的整数部分,可以记成四步:选择 Locale 创建 NumberFormat;用 NumberFormat.INTEGER_FIELD 创建 FieldPosition;调用带 StringBuffer 的 format 重载;读取 beginIndex 和 endIndex,按左闭右开区间截取。需要多个字段时,再升级到 formatToCharacterIterator。这样处理的好处是位置由格式器按本次输出计算,能适应分组符、负数和 Locale 差异。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python tokenize.untokenize 为什么不能保证恢复原始空格Python tokenize.untokenize 为什么不能保证恢复原始空格
上一篇
Python tokenize.untokenize 为什么不能保证恢复原始空格
getfacl 中 effective 权限为什么小于用户条目的权限
下一篇
getfacl 中 effective 权限为什么小于用户条目的权限
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    967次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    971次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1080次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1058次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    894次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码