统计规则生效与未生效

This commit is contained in:
yuejiajun 2025-09-28 13:16:25 +08:00
parent 175fbf93a2
commit 445e3e2ad5

View File

@ -7,6 +7,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -1388,8 +1390,8 @@ public class FormatUtil {
* <li>支持正则表达式替换和普通字符串替换</li> * <li>支持正则表达式替换和普通字符串替换</li>
* <li>可配置大小写敏感匹配</li> * <li>可配置大小写敏感匹配</li>
* <li>支持整词匹配word boundary</li> * <li>支持整词匹配word boundary</li>
* <li>提供正则表达式错误降级处理</li> * <li>提供详细的替换统计信息</li>
* <li>支持批量替换操作</li> * <li>记录未生效的替换规则</li>
* </ul> * </ul>
* *
* @param source 源字符串如果为null则直接返回null * @param source 源字符串如果为null则直接返回null
@ -1402,7 +1404,7 @@ public class FormatUtil {
* @param useRegex 是否使用正则表达式 * @param useRegex 是否使用正则表达式
* true: 将key作为正则表达式处理false: 作为普通字符串处理 * true: 将key作为正则表达式处理false: 作为普通字符串处理
* @return 替换后的字符串如果源字符串或替换映射表为null/空则返回原字符串 * @return 替换后的字符串如果源字符串或替换映射表为null/空则返回原字符串
* @throws IllegalArgumentException 当replacementMap包含null键或值时抛出 * @throws IllegalArgumentException 当replacementMap包含null键时抛出
*/ */
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap, public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap,
boolean caseSensitive, boolean wholeWord, boolean useRegex) { boolean caseSensitive, boolean wholeWord, boolean useRegex) {
@ -1432,41 +1434,238 @@ public class FormatUtil {
String result = source; String result = source;
int totalReplacements = 0; int totalReplacements = 0;
List<String> ineffectiveRules = new ArrayList<>(); // 记录未生效的规则
Map<String, Integer> replacementStats = new HashMap<>(); // 记录每个规则的替换次数
// 计算源字符串的SHA1用于精确比较变化
String sourceHash = calculateSHA1(source);
String currentResult = source;
String currentHash = sourceHash;
// 遍历替换映射表按顺序应用每个替换规则 // 遍历替换映射表按顺序应用每个替换规则
for (Map.Entry<String, String> entry : replacementMap.entrySet()) { for (Map.Entry<String, String> entry : replacementMap.entrySet()) {
String patternStr = entry.getKey(); String patternStr = entry.getKey();
String replacement = entry.getValue() != null ? entry.getValue() : ""; String replacement = entry.getValue() != null ? entry.getValue() : "";
String previousHash = currentHash; // 记录应用规则前的hash
try { try {
if (useRegex) { if (useRegex) {
// 使用正则表达式替换 // 使用正则表达式替换
log.info("应用替换规则(正则表达式替换): '{}' -> '{}'", patternStr, replacement); log.debug("应用替换规则(正则表达式替换): '{}' -> '{}'", patternStr, replacement);
result = applyRegexReplacement(result, patternStr, replacement, caseSensitive, wholeWord); result = applyRegexReplacement(result, patternStr, replacement, caseSensitive, wholeWord, replacementStats);
} else { } else {
// 使用普通字符串替换 // 使用普通字符串替换
log.info("应用替换规则(普通字符串替换): '{}' -> '{}'", patternStr, replacement); log.debug("应用替换规则(普通字符串替换): '{}' -> '{}'", patternStr, replacement);
result = applyLiteralReplacement(result, patternStr, replacement, caseSensitive, wholeWord); result = applyLiteralReplacement(result, patternStr, replacement, caseSensitive, wholeWord, replacementStats);
} }
// 统计替换次数通过比较字符串长度变化估算 // 计算当前结果的hash值
if (!result.equals(source)) { currentResult = result;
currentHash = calculateSHA1(currentResult);
// 统计替换次数通过SHA1比较判断是否发生变化
boolean hasChanged = !currentHash.equals(previousHash);
int ruleReplacementCount = replacementStats.getOrDefault(patternStr, 0);
if (hasChanged && ruleReplacementCount > 0) {
totalReplacements++; totalReplacements++;
log.info("规则 '{}' 生效,替换次数: {}", patternStr, ruleReplacementCount);
} else {
// 记录未生效的规则
ineffectiveRules.add(patternStr);
log.info("规则 '{}' 未生效,未找到匹配项", patternStr);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("替换规则处理失败 - 模式: '{}', 替换值: '{}', 错误: {}", log.error("替换规则处理失败 - 模式: '{}', 替换值: '{}', 错误: {}",
patternStr, replacement, e.getMessage()); patternStr, replacement, e.getMessage());
// 发生错误时该规则视为未生效
ineffectiveRules.add(patternStr);
// 发生错误时继续处理下一个规则不中断整个流程 // 发生错误时继续处理下一个规则不中断整个流程
} }
} }
log.info("字符串替换完成,总应用规则数: {}, 实际产生变化的规则数: {}", // 输出详细的统计信息
replacementMap.size(), totalReplacements); log.info("字符串替换完成,总应用规则数: {}, 实际产生变化的规则数: {}, 总替换次数: {}",
replacementMap.size(), totalReplacements, calculateTotalReplacements(replacementStats));
// 输出未生效的规则DEBUG级别
if (!ineffectiveRules.isEmpty() && log.isDebugEnabled()) {
log.info("以下 {} 个规则未生效: {}", ineffectiveRules.size(), ineffectiveRules);
}
// 输出每个规则的详细替换统计DEBUG级别
if (log.isDebugEnabled()) {
log.info("详细替换统计:");
for (Map.Entry<String, Integer> stat : replacementStats.entrySet()) {
String rule = stat.getKey();
int count = stat.getValue();
String status = count > 0 ? "生效" : "未生效";
log.info("规则 '{}': {}次替换 - {}", rule, count, status);
}
}
return result; return result;
} }
/**
* 计算字符串的SHA1哈希值
*
* @param input 输入字符串
* @return SHA1哈希值如果输入为null则返回空字符串
*/
private static String calculateSHA1(String input) {
if (input == null) {
return "";
}
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
log.warn("SHA1算法不可用使用简单hash替代", e);
// 降级方案使用简单的hashCode
return Integer.toHexString(input.hashCode());
}
}
/**
* 应用正则表达式替换并统计替换次数
*/
private static String applyRegexReplacement(String source, String regex, String replacement,
boolean caseSensitive, boolean wholeWord,
Map<String, Integer> stats) {
String processedRegex = regex;
// 处理整词匹配
if (wholeWord && !processedRegex.startsWith("\\b") && !processedRegex.contains("\\b")) {
processedRegex = "\\b" + processedRegex + "\\b";
log.trace("添加单词边界,处理后的正则: {}", processedRegex);
}
try {
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
Pattern pattern = Pattern.compile(processedRegex, flags);
Matcher matcher = pattern.matcher(source);
// 先统计匹配次数
int count = 0;
while (matcher.find()) {
count++;
}
// 记录统计信息
stats.put(regex, count);
// 执行替换
if (count > 0) {
matcher.reset();
return matcher.replaceAll(replacement);
} else {
return source; // 无匹配返回原字符串
}
} catch (Exception e) {
log.warn("正则表达式编译或执行失败,降级为普通字符串替换 - 模式: '{}', 错误: {}",
regex, e.getMessage());
// 降级处理使用普通字符串替换
return applyLiteralReplacement(source, regex, replacement, caseSensitive, wholeWord, stats);
}
}
/**
* 应用普通字符串替换并统计替换次数
*/
private static String applyLiteralReplacement(String source, String literal, String replacement,
boolean caseSensitive, boolean wholeWord,
Map<String, Integer> stats) {
if (wholeWord) {
// 整词匹配处理使用正则表达式实现单词边界匹配
try {
String regex = "\\b" + Pattern.quote(literal) + "\\b";
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
Pattern pattern = Pattern.compile(regex, flags);
Matcher matcher = pattern.matcher(source);
// 统计匹配次数
int count = 0;
while (matcher.find()) {
count++;
}
// 记录统计信息
stats.put(literal, count);
// 执行替换
if (count > 0) {
matcher.reset();
return matcher.replaceAll(replacement);
} else {
return source; // 无匹配返回原字符串
}
} catch (Exception e) {
log.warn("整词匹配处理失败,使用普通替换 - 模式: '{}'", literal);
// 降级为普通替换
}
}
// 普通字符串替换
int count;
String result;
if (caseSensitive) {
// 大小写敏感替换
result = source.replace(literal, replacement);
// 计算替换次数通过字符串分割方式
count = (source.length() - result.length()) / (literal.length() - replacement.length());
if (count < 0) count = 0; // 处理除数为0的情况
} else {
// 大小写不敏感替换
result = replaceIgnoreCase(source, literal, replacement);
count = countIgnoreCaseOccurrences(source, literal);
}
// 记录统计信息
stats.put(literal, count);
return result;
}
/**
* 计算字符串中不区分大小写的出现次数
*/
private static int countIgnoreCaseOccurrences(String source, String target) {
if (source == null || target == null || target.isEmpty()) {
return 0;
}
int count = 0;
String lowerSource = source.toLowerCase();
String lowerTarget = target.toLowerCase();
int index = 0;
while ((index = lowerSource.indexOf(lowerTarget, index)) != -1) {
count++;
index += target.length();
}
return count;
}
/**
* 计算总替换次数
*/
private static int calculateTotalReplacements(Map<String, Integer> stats) {
return stats.values().stream().mapToInt(Integer::intValue).sum();
}
/** /**
* 应用正则表达式替换 * 应用正则表达式替换
* *