模糊查找文本
1、定义忽略字符,如“的吗了啊吧”及所有符号
2、把原始文本中的忽略字符去掉并记下位置
3、把目标文本中的忽略字符替换成正则.{0,3}
4、用正则匹配处理后的原始文本,得到开始/结束位置
5、开始/结束位置加上在此之间被删除的字符数
6、如果目标文本最后是忽略字符,从后向前查找最后一个字符, 或标点符号
容错性有点差,还得另想办法~~
public class SearchSubtextTest {
public static void main(String[] args) {
String source = "持有人会议机制:本期债务融资工具募集说明书在【持有人会议机制】章节中设置了对投资者的实体权利影响较大的特别议案,按照本募集说明书约定,特别议案决议生效条件为持有本期债务融资工具表决权超过总表决权数额 90% 的持有人同意。因此,存在特别议案未经全体投资人同意而生效的情况下,个别投资人虽不同意但已受生效特别议案的约束,自身实体权益存在因服从绝大多数人利益可能受到不利影响的可能性,特别议案如下:";
// 差异: # # + - #### - #
String target = "本期债务融资工具募集说明书在“持有人会议机制”章节中设置了对投资者实体权利影响较大的特别议案,按照本募集说明书约定,特别议案的决议生效条件为持有本期债务融资工具表决权超过总表决权数额【___】的持有人同意。因此,存在特别议案未经全体投资人同意而生效的情况下,个别投资人虽不同意但已受生效特别议案的约束,______等自身实体权益存在因服从绝大多数人利益可能受到不利影响的可能性。";
String ignore = "【】[]{}“”,:!?,。:!? \t\n的吗等了";
System.out.println(SearchSubtextTools.searchSubtext(source, target, ignore));
}
}
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 模糊查找文本
*
* @author zhaohuihua
* @version 20210804
*/
public class SearchSubtextTools {
public static void main(String[] args) {
String source = "持有人会议机制:本期债务融资工具募集说明书在【持有人会议机制】章节中设置了对投资者的实体权利影响较大的特别议案,按照本募集说明书约定,特别议案决议生效条件为持有本期债务融资工具表决权超过总表决权数额 90% 的持有人同意。因此,存在特别议案未经全体投资人同意而生效的情况下,个别投资人虽不同意但已受生效特别议案的约束,自身实体权益存在因服从绝大多数人利益可能受到不利影响的可能性,特别议案如下:";
// 差异: # # + - #### - #
String target = "本期债务融资工具募集说明书在“持有人会议机制”章节中设置了对投资者实体权利影响较大的特别议案,按照本募集说明书约定,特别议案的决议生效条件为持有本期债务融资工具表决权超过总表决权数额【___】的持有人同意。因此,存在特别议案未经全体投资人同意而生效的情况下,个别投资人虽不同意但已受生效特别议案的约束,______等自身实体权益存在因服从绝大多数人利益可能受到不利影响的可能性。";
String ignore = "【】[]{}“”,:!?,。:!? \t\n的吗等了";
System.out.println(searchSubtext(source, target, ignore));
}
/**
* 模糊查找文本<br>
* 1、定义忽略字符,如“的吗了啊吧”及所有符号<br>
* 2、把原始文本中的忽略字符去掉并记下位置<br>
* 3、把目标文本中的忽略字符替换成正则.{0,3}<br>
* 4、用正则匹配处理后的原始文本,得到开始/结束位置<br>
* 5、开始/结束位置加上在此之间被删除的字符数<br>
* 6、如果目标文本最后是忽略字符,从后向前查找最后一个字符, 或标点符号
*
* @param text 原始文本
* @param target 目标文本
* @param ignoreChars 忽略字符
* @return 关键字及位置信息
*/
public static SubtextInfo searchSubtext(String text, String target, String ignoreChars) {
if (text == null || text.length() == 0 || target == null || target.length() == 0) {
return null;
}
if (ignoreChars == null || text.length() == 0) {
// 没有忽略字符, 直接使用indexOf比对
int index = text.indexOf(target);
if (index < 0) {
return null;
}
return new SubtextInfo(target, index, index + target.length());
}
// 删除原文中的忽略字符, 并记录下位置
Map<Integer, Character> removedChars = new HashMap<>();
StringBuffer source = new StringBuffer();
for (int i = 0, z = text.length(); i < z; i++) {
char c = text.charAt(i);
if (ignoreChars.indexOf(c) >= 0) {
removedChars.put(source.length(), c);
} else {
source.append(c);
}
}
// 将目标文本中的忽略字符替换为正则表达式.{0,n}
String regexpChars = "{}[]()^$.*?-+|";
StringBuilder buffer = new StringBuilder();
int ignoreLength = 0;
char lastChar = 0;
for (int i = 0, z = target.length(); i < z; i++) {
char c = target.charAt(i);
if (c == '_') {
ignoreLength += 3;
} else if (ignoreChars.indexOf(c) >= 0) {
ignoreLength += 3;
} else {
if (ignoreLength > 0) {
buffer.append(".{0,").append(Math.max(5, ignoreLength)).append("}");
ignoreLength = 0;
}
if (regexpChars.indexOf(c) >= 0) {
buffer.append("\\");
}
buffer.append(c);
lastChar = c;
}
}
boolean endsWithIgnoreChars = false;
if (ignoreLength > 0) {
endsWithIgnoreChars = true;
buffer.append(".{0,").append(Math.max(5, ignoreLength)).append("}");
}
Pattern pattern = Pattern.compile(buffer.toString());
Matcher matcher = pattern.matcher(source);
if (!matcher.find()) {
return null;
}
int min = matcher.start();
int max = matcher.end();
int start = min;
int end = max;
// 在min和max之间被删除的字符, 加到start和end中
for (int index : removedChars.keySet()) {
if (index <= min) {
start++;
end++;
}
if (index < max) {
end++;
}
}
if (endsWithIgnoreChars) {
String symbol = ";,。:!?:;!?";
// 从后向前查找最后一个字符, 或标点符号
for (; end > start && end > 1; end--) {
char c = text.charAt(end - 1);
if (c == lastChar) {
break;
} else if (symbol.indexOf(c) >= 0) {
break;
}
}
}
String substring = text.substring(start, end);
return new SubtextInfo(substring, start, end);
}
/** 子文本信息 **/
public static class SubtextInfo {
/** 子文本 **/
private final String subtext;
/** 子文本在原文本的开始位置 **/
private final Integer startIndex;
/** 子文本在原文本的结束位置 **/
private final Integer endIndex;
public SubtextInfo(String subtext, int start, int end) {
this.subtext = subtext;
this.startIndex = start;
this.endIndex = end;
}
/** 子文本 **/
public String getSubtext() {
return subtext;
}
/** 子文本在原文本的开始位置 **/
public Integer getStartIndex() {
return startIndex;
}
/** 子文本在原文本的结束位置 **/
public Integer getEndIndex() {
return endIndex;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append(pad(startIndex, 3));
buffer.append(':').append(pad(endIndex, 3));
buffer.append(" ").append(subtext);
return buffer.toString();
}
/** 左侧补零 **/
private static String pad(int number, int length) {
char c = '0';
String string = String.valueOf(number);
char[] array = new char[length - string.length()];
Arrays.fill(array, c);
return new String(array) + string;
}
}
}