
Java 中 String.contains() 无法区分 *、** 和 ***,因为它执行子串精确匹配;要精准识别行首连续 n 个星号,需改用正则表达式或手动计数逻辑。
java 中 `string.contains()` 无法区分 `*`、`**` 和 `***`,因为它执行子串精确匹配;要精准识别行首连续 n 个星号,需改用正则表达式或手动计数逻辑。
在文本处理中,若需根据行首星号数量(如 *、**、***)进行分类、提取或拼接操作,直接使用 str.contains("***") 是错误的起点——它仅判断 *** 是否作为子串存在,不关心位置、连续性或上下文,更无法排除 * 单独出现导致的误匹配。
例如:
System.out.println("*Lorem ipsum".contains("***")); // false —— 正确,不匹配
System.out.println("ex***".contains("***")); // true —— 但这是末尾匹配,非行首意图
System.out.println("***Aliquam".contains("***")); // true —— 符合需求,但无法区分 "***" 和 "****"
可见 contains() 缺乏位置约束与边界控制,无法满足“行首连续恰好三个星号”的语义。
✅ 推荐方案:使用正则表达式精准锚定
最清晰、可维护的方式是借助 Pattern 和 Matcher,配合 ^(行首)和量词 {n}:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class AsteriskMatcher {
// 匹配行首连续恰好3个星号(后跟非星号字符或行尾)
private static final Pattern THREE_ASTERISKS = Pattern.compile("^\*{3}(?![*])");
public static boolean startsWithExactlyThreeAsterisks(String line) {
if (line == null) return false;
Matcher m = THREE_ASTERISKS.matcher(line.trim());
return m.find();
}
public static int countLeadingAsterisks(String line) {
if (line == null) return 0;
int count = 0;
for (char c : line.toCharArray()) {
if (c == '*') count++;
else break;
}
return count;
}
public static void main(String[] args) {
String[] lines = {
"Lorem ipsum dolor sit amet,",
"**consectetur adipiscing elit.",
"***Aliquam imperdiet iaculis ex",
"ex***", // 注意:这不是行首匹配
"****Quaternary level"
};
for (String line : lines) {
int stars = countLeadingAsterisks(line);
System.out.printf("'%s' → %d leading asterisks%n",
line.replace("
", "\n"), stars);
if (stars == 3) {
System.out.println("→ Will be concatenated with original content.");
}
}
}
}
? 关键说明:
-
^\*{3}(?![*])中^确保匹配从行首开始,\*{3}匹配恰好三个字面星号(*是正则元字符,必须转义),(?![*])是负向先行断言,确保第四个字符不是星号,从而排除****的误匹配。 -
countLeadingAsterisks()是轻量替代方案,适用于性能敏感场景,逻辑直观且无正则开销。 - 若需处理多行文本,建议先用
String.split("\R")或Files.readAllLines()拆分为行列表,再逐行校验——避免跨行干扰。
⚠️ 注意事项:
- 切勿依赖
contains()判断“以 n 个星号开头”,它不具备位置感知能力; - 使用正则时务必转义
*(写作\*),否则会触发通配含义; - 对于纯前缀计数,
indexOf()循环虽可行,但可读性与健壮性不如显式遍历或正则; - 实际业务中,建议将星号层级解析为结构化数据(如
LevelledLine(level: 3, content: "Aliquam...")),便于后续渲染或转换。
通过正则锚定或手动前缀扫描,即可稳定、可扩展地实现星号层级识别,彻底规避 contains() 带来的语义歧义。










