我必须解析一些html,从一些HTML中找到一组值,这些值并不总是很好地形成,我无法控制(所以Scanner似乎不是一个选项)
这是一个购物车,购物车内有n行,每行包含一个数量下拉列表。现在我希望能够获得购物车中产品的总和。
鉴于此html,我希望匹配值2和5
...
<select attr="other stuff" name="quantity">
<option value="1" />
<option value="2" selected="selected" />
</select>
....
<select name="quantity" attr="other stuff">
<option selected="selected" value="5" />
<option value="6" />
</select>
我做了一些可怜的尝试,但考虑到变量的数量(例如,&#39;值&#39;以及&#39;选择&#39;标签的顺序)我的大多数解决方案要么不工作或真的很慢。
我结束的最后一个Java代码是以下
Pattern pattern = Pattern.compile("select(.*?)name=\"quantity\"([.|\\n|\\r]*?)option(.*?)value=\"(/d)\" selected=\"selected\"", Pattern.DOTALL);
Matcher matcher = pattern.matcher(html);
if (matcher.find()) {
....
}
它非常慢,并且在属性顺序更改时不起作用。我的正则表达式知识不足以写出有效的模式
答案 0 :(得分:4)
您可以使用XPath表达式来检索问题中的HTML的所有值属性,而不是使用正则表达式:
//select[@name="quantity"]/option[@selected="selected"]/@value
用语言说:
<select>
等于name
的XML中的所有quantity
元素,其中子元素<option>
的属性selected
等于{{1} }} selected
属性。我真的会考虑尝试使用XQuery / XPath,这是它的用途。请阅读问题this answer的How to read XML using XPath in Java,了解如何检索值。关于XPath表达式here的介绍。
考虑以下情况,您将来只需要找到属性value
和的选项,例如selected="selected"
。 XPath表达式将简单地变为:
status="accepted"
XPath表达式易于扩展,易于查看,易于证明它正在做什么。
现在你需要为增加的条件创建什么样的RegEx怪物?很难写,甚至更难维护。代码审查员如何判断正则表达式的复杂性(cf bobble bubble&answer)?你如何证明正则表达式实际上正在做它应该做的事情?
您当然可以记录正则表达式,这是您应该对正则表达式执行的操作。但这并没有证明什么。
我的建议:远离正则表达式,除非绝对没有办法。
对于体育运动,我制作了一个片段,展示了这种工作方式的基础知识:
//select[@name="quantity"]/option[@selected="selected" and @status="accepted"]/@value
输出结果:
import java.io.StringReader;
import javax.xml.xpath.*;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class ReadElementsFromHtmlUsingXPath {
private static final String html=
"<html>Read more about XPath <a href=\"www.w3schools.com/xsl/xpath_intro.asp\">here</a>..."+
"<select attr=\"other stuff\" name=\"quantity\">"+
"<option value=\"1\" />"+
"<option value=\"2\" selected=\"selected\" />"+
"</select>"+
"<i><b>Oh and here's the second element</b></i>"+
"<select name=\"quantity\" attr=\"other stuff\">"+
"<option selected=\"selected\" value=\"5\" />"+
"<option value=\"6\" />"+
"</select>"+
"And that's all folks</html>";
private static final String xpathExpr =
"//select[@name=\"quantity\"]/option[@selected=\"selected\"]/@value";
public static void main(String[] args) {
try {
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile(xpathExpr);
NodeList nodeList = (NodeList) expr.evaluate(new InputSource(new StringReader(html)),XPathConstants.NODESET);
for( int i = 0; i != nodeList.getLength(); ++i )
System.out.println(nodeList.item(i).getNodeValue());
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
}
答案 1 :(得分:3)
当然取决于您的HTML格式是多么糟糕。 Parser solution是首选。
符合您要求的正则表达式并不是一个挑战,只需将它组合在一起。
(?xi) # i-flag for caseless, x-flag for comments (free spacing mode)
# 1.) match <select with optional space at the end
<\s*select\s[^>]*?\bname\s*=\s*["']\s*quantity[^>]*>\s*
# 2.) match lazily any amount of options until the "selected"
(?:<\s*option[^>]*>\s*)*?
# 3.) match selected using a lookahead and capture number from value
<\s*option\s(?=[^>]*?\bselected)[^>]*?\bvalue\s*=\s*["']\s*(\d[.,\d]*)
Try pattern at regex101或RegexPlanet (Java)以及Java字符串:
"(?i)<\\s*select\\s[^>]*?\\bname\\s*=\\s*[\"']\\s*quantity[^>]*>\\s*(?:<\\s*option[^>]*>\\s*)*?<\\s*option\\s(?=[^>]*?\\bselected)[^>]*?\\bvalue\\s*=\\s*[\"']\\s*(\\d[.,\\d]*)"
它没有多少魔力。一个长丑陋的模式,主要是因为解析html。
\s
是空格[ \t\r\n\f]
\d
是数字[0-9]
\b
匹配word boundary (?:
打开non capturing group [^>]
是>
的{{3}}(匹配字符,不是>
)(?=[^>]*?\bselected)
使用negation完成检查,以便独立于订单(\d[.,\d]*)
部分来捕获数字。必填项是一位数字,带有任意可选[.,\d]
匹配将在group(1)
第一个lookahead(带括号的组)中。
答案 2 :(得分:2)
首先,创建一个名为Option
的类:
public class Option {
private String value;
private boolean selected;
public Option() {
}
public Option(String value, boolean selected) {
this.value = value;
this.selected = selected;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public String toString() {
return "{" +
"value='" + value + '\'' +
", selected=" + selected +
'}';
}
}
第二,我们需要一个regex
来找到html标签:
static final Pattern OPTION_TAG_PATTERN = Pattern.compile("<option\\s*(value=\"\\w+\"\\s+(?:selected=\"selected\")?|(?:selected=\"selected\")?\\s+value=\"\\w+\")\\s*/>");
并提取value
的价值:
static final Pattern VALUE_PATTERN = Pattern.compile("value=\"(\\w+)\"");
最后:
public class Test {
private static final Pattern OPTION_TAG_PATTERN = Pattern.compile("<option\\s*(value=\"\\w+\"\\s+(?:selected=\"selected\")?|(?:selected=\"selected\")?\\s+value=\"\\w+\")\\s*/>");
private static final Pattern VALUE_PATTERN = Pattern.compile("value=\"(\\w+)\"");
public static void main(String[] args) {
String html = "...\n" +
"<select attr=\"other stuff\" name=\"quantity\">\n" +
" <option value=\"1\" />\n" +
" <option value=\"2\" selected=\"selected\" />\n" +
"</select>\n" +
"....\n" +
"<select name=\"quantity\" attr=\"other stuff\">\n" +
" <option selected=\"selected\" value=\"5\" />\n" +
" <option value=\"6\" />\n" +
"</select>";
findOptions(html).forEach(System.out::println);
}
public static List<Option> findOptions(String htmlContent) {
List<Option> options = new ArrayList<>();
Matcher optionMatcher = OPTION_TAG_PATTERN.matcher(htmlContent);
while (optionMatcher.find()) {
options.add(toOption(htmlContent.substring(optionMatcher.start(), optionMatcher.end())));
}
return options;
}
private static Option toOption(String htmlTag) {
Option option = new Option();
Matcher valueMatcher = VALUE_PATTERN.matcher(htmlTag);
if (valueMatcher.find()) {
option.setValue(valueMatcher.group(1));
}
if (htmlTag.contains("selected=\"selected\"")) {
option.setSelected(true);
}
return option;
}
}
输出:
{value='1', selected=false}
{value='2', selected=true}
{value='5', selected=true}
{value='6', selected=false}
我希望这可以帮到你!
答案 3 :(得分:0)
我认为正则表达式并不是最好的,因为复杂性使得难以通读和诊断代码。我们仍然可以使用正则表达式,但要打破逻辑,以便更容易阅读和改进:
String html = "<select attr=\"other stuff\" name=\"quantity\">" +
"<option value=\"1\" /> " +
"<option value=\"2\" selected=\"selected\" /> " +
"</select> " +
"<select name=\"quantity\" attr=\"other stuff\"> " +
"<option selected=\"selected\" value=\"5\" /> " +
"<option value=\"6\" /> " + "</select>";
String options = "(?<=<option).*?(?=/>)";
Pattern pat = Pattern.compile(options, Pattern.DOTALL);
Matcher m = pat.matcher(html);
Pattern values = Pattern.compile("(?<=value=\").*?(?=\")");
Pattern selected = Pattern.compile("selected=\"selected\"");
Integer counter = 0;
while (m.find()) {
Matcher sel = selected.matcher(m.group());
if (sel.find()) {
Matcher val = values.matcher(m.group());
if (val.find()) {
Integer count = Integer.parseInt(val.group());
counter = counter + count;
}
}
}
System.out.println(counter.toString());
}
打印出所需的7