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

Java 如何获取字符串中的首个可打印字符

Java 获取字符串首个可打印字符方案

核心实现思路

你需要的「首个可打印字符」本质是Unicode标准定义的字形簇(Grapheme Cluster),即视觉上呈现为单个字符的单元,不管它底层占用多少码点、多少字节。Java 内置的BreakIterator工具类已经实现了字形簇的拆分规则,可以直接适配单字节普通字符、多字节Emoji、多码点组合国旗Emoji等所有场景。

完整实现代码

import java.text.BreakIterator;
import java.util.Locale;

public class PrintableCharUtils {

    public static String getFirstPrintableGrapheme(String input) {
        // 空值提前返回
        if (input == null || input.isEmpty()) {
            return "";
        }
        // 初始化字形簇迭代器,使用ROOT locale适配全球所有字符规则
        BreakIterator graphemeBreaker = BreakIterator.getCharacterInstance(Locale.ROOT);
        graphemeBreaker.setText(input);

        int start = graphemeBreaker.first();
        int end = graphemeBreaker.next();
        while (end != BreakIterator.DONE) {
            String currentGrapheme = input.substring(start, end);
            // 过滤非打印字符:控制符、格式字符、单独代理对,可根据需求调整过滤规则
            boolean isPrintable = currentGrapheme.codePoints().noneMatch(cp ->
                    Character.isISOControl(cp)
                    || Character.getType(cp) == Character.FORMAT
                    || Character.getType(cp) == Character.CONTROL
                    || Character.getType(cp) == Character.SURROGATE
            );
            // 不需要跳过空白字符可删除 !currentGrapheme.isBlank() 判断
            if (isPrintable && !currentGrapheme.isBlank()) {
                return currentGrapheme;
            }
            start = end;
            end = graphemeBreaker.next();
        }
        // 全为非打印字符时返回空
        return "";
    }

    public static void main(String[] args) {
        // 测试示例
        System.out.println(getFirstPrintableGrapheme("abcd")); // 输出a
        System.out.println(getFirstPrintableGrapheme("😀😀")); // 输出😀
        System.out.println(getFirstPrintableGrapheme("☠😀☠")); // 输出☠
        System.out.println(getFirstPrintableGrapheme("🇮🇳😀")); // 输出🇮🇳
        System.out.println(getFirstPrintableGrapheme("🇮🇳abcd")); // 输出🇮🇳
        // 验证length特性
        System.out.println("😀".length()); // 输出2,对应UTF-16编码的2个char单元
        System.out.println("😀".codePointCount(0, "😀".length())); // 输出1,对应1个Unicode码点
    }
}

原理解释

你观察到的"😀".length() == 2是因为Java的String#length()返回的是字符串在UTF-16编码下的char单元数量,而Emoji😀对应的Unicode码点是U+1F600,属于超出BMP平面的增补字符,需要用2个char(高代理+低代理)存储,所以返回2。如果要获取字符串的实际码点数量,需要调用codePointCount(0, str.length())方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 01:27:03