给定字符串中的混合重音字符和普通字符在搜索时在 java 中不起作用
Posted
技术标签:
【中文标题】给定字符串中的混合重音字符和普通字符在搜索时在 java 中不起作用【英文标题】:Given mixed accented and normal characters in string not working in java when searching 【发布时间】:2019-03-21 00:07:40 【问题描述】:String text = "Cámélan discovered ônte red aleŕt \n Como se extingue la deuda";
如果我给出输入 Ca,它应该从给定的字符串 Cá 中突出显示,但它没有突出显示。
以下是我尝试过的。
Pattern mPattern;
String filterTerm; //this is the input which I give from input filter. Say for eg: Ca
String regex = createFilterRegex(filterTerm);
mPattern = Pattern.compile(regex);
private String createFilterRegex(String filterTerm)
filterTerm = Normalizer.normalize(filterTerm, Normalizer.Form.NFD);
filterTerm = filterTerm.replaceAll("[\\pInCombiningDiacriticalMarks]", "");
return filterTerm;
public Pattern getPattern()
return mPattern;
在另一个班级,
private SpannableStringBuilder createHighlightedString(String nodeText, int highlightColor) //nodeText is the entire list displaying.
SpannableStringBuilder returnValue = new SpannableStringBuilder(nodeText);
String lowercaseNodeText = nodeText;
Matcher matcher = mFilter.getPattern().matcher((createFilterRegex(lowercaseNodeText)));
while (matcher.find())
returnValue.setSpan(new ForegroundColorSpan(highlightColor), matcher.start(0),
matcher.end(0), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
return returnValue;
viewHolder.mTextView.setText(createHighlightedString((node.getText()), mHighlightColor));
但是我得到的输出是,
如果我单独输入单个字母 o,它会突出显示,但如果我传递两个以上的字母,例如:Ca,它不会突出显示和显示。我无法弄清楚我在做什么错误。
但是,如果您查看 WhatsApp。它已经实现了。
我输入了 Co,它可以识别和突出句子中的重音字符。
【问题讨论】:
你尝试过我的解决方案@Star 吗? @PradyumanDixit 是的。但还是不行。 @Star 有什么问题? @PradyumanDixit 问题是当我在内容中搜索为 Ca 时,它不接受这个 Cá。只有当我搜索 Cá 时它才会接受。 您是否使用了我在答案中给出的两个代码来使字符串忽略重音字符? 【参考方案1】:如你所说,
String text = "Cámélan 发现 ônte red aleŕt \n Como se extingue la deuda";
因此,每当您给出第一个输入时,都会接收第一个字符并进行比较。
例如:如果你给Ca,那么
if (StringUtils.isNotEmpty(substring)) //this is the search text
substring=substring.substring(0,1); //now you get C alone.
因此,无论您键入什么,它都会通过过滤第一个字符来显示。现在
SpannableString builder = higlightString((yourContent.getText()), mHighlightColor);
viewHolder.mTextView.setText(builder);
private SpannableString higlightString(String entireContent, int highlightColor)
SpannableString returnValue = new SpannableString(entireContent);
String lowercaseNodeText = entireContent;
try
Matcher matcher = mFilter.getPattern().matcher(((diacritical(lowercaseNodeText.toLowerCase()))));
while (matcher.find())
returnValue.setSpan(new ForegroundColorSpan(highlightColor), matcher.start(0),
matcher.end(0), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
catch (Exception e)
e.printStackTrace();
return returnValue;
private String diacritical(String original)
String removed=null;
String decomposed = Normalizer.normalize(original, Normalizer.Form.NFD);
removed = decomposed.replaceAll("\\pInCombiningDiacriticalMarks+", "");
return removed;
测试用例:
当您输入 Ca 时,它通过显示所有 C 内容进入整个文本获取所有数据并通过对内容进行规范化过滤掉,它也与重音字符匹配并通过高亮显示。
【讨论】:
【参考方案2】:我不是 Java 程序员,所以这里只是一些基本的原始正则表达式解决方案。
如果你能规范化字符串的分解形式 假设是这个
String sSourceTargetDecom = Normalizer.normalize(sourcetarget, Normalizer.Form.NFD);
,
这应该变成0000C1 Á LATIN CAPITAL LETTER A WITH ACUTE
分成两个字符A
和000301 ́ COMBINING ACUTE ACCENT
。
您可以使用
从块中获取最多的组合字符[\pBlock=Combining_Diacritical_Marks\pBlock=Combining_Diacritical_Marks_Extended\pBlock=Combining_Diacritical_Marks_For_Symbols\pBlock=Combining_Diacritical_Marks_Supplement\pBlock=Combining_Half_Marks]
十六进制范围为
[\x300-\x36f\x1ab0-\x1aff\x1dc0-\x1dff\x20d0-\x20ff\xfe20-\xfe2f]
事实证明,大多数与基本拉丁语相关的组合标记可以是
分解的在[\x300-\x36f]
范围内。
您可以分解两者源目标和输入搜索字符串。
然后从输入的搜索字符串创建一个正则表达式。
注入 [\x300-\x36f]?
在每个基本拉丁字母之后。
String regex = sSearch.replaceAll("([a-zA-Z])[\\x300-\\x36f]?", "\\1[\\x300-\\x36f]?");
(不确定 Java 在其正则表达式中使用什么代码点字符表示法,可能需要为 \uDD
然后在 sSourceTargetDecom 字符串上使用正则表达式,它将单独匹配基本拉丁语,和/或与可选的组合代码匹配。
【讨论】:
【参考方案3】:你已经得到了:
private String convertToBasicLatin(String text)
return Normalizer.normalize(text, Normalizer.Form.NFD)
.replaceAll("\\pM", "").replaceAll("\\R", "\n");
为了使 one 无重音的基本拉丁字符匹配 one 重音字母的 Unicode 代码点, 应该将 规范化为 composed 形式:
private String convertToComposedCodePoints(String text)
return Normalizer.normalize(text, Normalizer.Form.NFC).replaceAll("\\R", "\n");
一般来说,人们可能会假设 Unicode 代码点也是 1 个字符长。
搜索键使用 convertToBasicLatin(sought) 文本视图的内容使用 convertToComposedCodePoints(content) 匹配的文本内容使用convertToBasicLatin(content)现在start
和end
匹配器的索引位置是正确的。
我将\r\n
或\u0085
等明确的行尾(正则表达式\R
)标准化为单个\n
。
不能标准化为小写/大写,因为字符的数量可能会有所不同:
德语小写ß
对应大写SS
。
String sought = ...;
String content = ...;
sought = convertToBasicLatin(sought);
String latinContent = convertToBasicLatin(content);
String composedContent = convertToComposedUnicode(content);
Matcher m = Pattern.compile(sought, Pattern.CASE_INSENSITIVE
| Pattern.UNICODE_CASE | Pattern.UNICODE_CHARACTER_CLASS
| Pattern.UNIX_LINES)
.matcher(latinContent);
while (m.find())
... // One can apply `m.start()` and `m.end()` to composedContent of the view too.
【讨论】:
感谢@Joop Eggen。我可以知道为什么我不能使用 Pattern.UNICODE_CHARACTER_CLASS。如果我使用它,我会崩溃 [android] Unsupported Flag 256 for Pattern.java 放弃那个标志,不同的java实现。有利于其他人注意。以上是关于给定字符串中的混合重音字符和普通字符在搜索时在 java 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章