如何使用 C# 搜索连续 XML 个节点的值?
How can I search values of consecutive XML nodes with C#?
我想 select 来自 XML 的节点,这些节点具有连续的子节点,其值与我的搜索词中的相应词相匹配。
这是一个示例 XML:
<book name="Nature">
<page number="4">
<line ln="10">
<word wn="1">a</word>
<word wn="2">white</word>
<word wn="3">bobcat</word>
<word wn="3">said</word>
</line>
<line ln="11">
<word wn="1">Hi</word>
<word wn="2">there,</word>
<word wn="3">Bob.</word>
</line>
</page>
我的搜索词是嗨鲍勃。我想从上面的 XML 中找到所有包含两个 consecutive 单词且值为 %Hi% 和 的节点%鲍勃%。请注意,我想对搜索词中的每个词执行不区分大小写的部分匹配。
上面XML应该return输出如下:
ln="10" wn="2" wn="3"
请注意,第 (ln=10) 行是 select 编辑的,因为它包含两个与搜索词匹配的连续单词(顺序正确)。白=%Hi%山猫=%Bob%
但是,下一行 (ln=11) 没有被 select 编辑,因为匹配的节点不连续。
请注意,搜索词中的所有字词都应匹配才能被视为匹配项。
谢谢!
[编辑]
我尝试了以下解决方案,它产生了预期的结果。有没有更好或更有效的方法来实现这一目标?该程序每天必须搜索 100,000 个 XML 个文件,每个文件的大小为 300 KB 到 50 MB。
XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
var xLines = xDoc
.Descendants("page")
.Descendants("line");
foreach (var xLine in xLines)
{
var xFirstWords = xLine
.Descendants("word")
.Where(item => item.Value.ToUpper().Contains("HI"));
foreach (var xFirstWord in xFirstWords)
{
var xNextWord = xFirstWord.NodesAfterSelf().OfType<XElement>().First();
if(xNextWord.Value.ToUpper().Contains("BOB"))
{
MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
}
}
}
我可以即兴创作我的代码。如果您有更好的解决方案,请告诉我。
XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
var xLines = xDoc
.Descendants("page")
.Descendants("line");
foreach (var xLine in xLines)
{
var xFirstWords = xLine
.Descendants("word")
.Where(item => item.Value.ToUpper().Contains("HI"))
.Where(item => item.ElementsAfterSelf("word").First().Value.ToUpper().Contains("BOB"));
foreach (var xFirstWord in xFirstWords)
{
var xNextWord = xFirstWord.ElementsAfterSelf("word").First();
MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
}
}
我不知道这段代码的性能会更好还是更差,但我很确定它会有所不同,因此可能值得一试。重构该行的文字,然后用正则表达式匹配。
Regex re = new Regex(@"^.*Hi\s+\S+\s+Bob$*", RegexOptions.IgnoreCase);
XDocument xDoc = XDocument.Load(@"C:\Users\user\Documents\temp.xml");
foreach (XElement xLine in xDoc.Root.Descendants("line")) {
string text = string.Join(" ", xLine.Elements("word").Select(x => x.Value));
if (re.IsMatch(text)) {
Console.WriteLine(text);
}
}
在性能方面想到的事情:
.Nodes
将比 .Descendants
更快,因为它只获取直接子项。
- 使用
IndexOf
和 OrdinalIgnoreCase
而不是 ToUpper.Contains
。
- 在
foreach
而不是NodesAfterSelf
中,你可以只持有前一个节点。
var xLines = xDoc.Descendants("line");
foreach (var xLine in xLines)
{
XNode prevWord = null;
foreach (var word in xLine.Nodes("word"))
{
if(prevWord == null && word.Value.IndexOf("HI", StringComparison.OrdinalIgnoreCase))
{
prevWord == word;
}
else if(prevWord != null && word.Value.IndexOf("BOB"), StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show(xLine.FirstAttribute.Value + " " + prevWord.FirstAttribute.Value + " " + word.FirstAttribute.Value);
}
}
}
我想 select 来自 XML 的节点,这些节点具有连续的子节点,其值与我的搜索词中的相应词相匹配。
这是一个示例 XML:
<book name="Nature">
<page number="4">
<line ln="10">
<word wn="1">a</word>
<word wn="2">white</word>
<word wn="3">bobcat</word>
<word wn="3">said</word>
</line>
<line ln="11">
<word wn="1">Hi</word>
<word wn="2">there,</word>
<word wn="3">Bob.</word>
</line>
</page>
我的搜索词是嗨鲍勃。我想从上面的 XML 中找到所有包含两个 consecutive 单词且值为 %Hi% 和 的节点%鲍勃%。请注意,我想对搜索词中的每个词执行不区分大小写的部分匹配。
上面XML应该return输出如下:
ln="10" wn="2" wn="3"
请注意,第 (ln=10) 行是 select 编辑的,因为它包含两个与搜索词匹配的连续单词(顺序正确)。白=%Hi%山猫=%Bob%
但是,下一行 (ln=11) 没有被 select 编辑,因为匹配的节点不连续。
请注意,搜索词中的所有字词都应匹配才能被视为匹配项。
谢谢!
[编辑] 我尝试了以下解决方案,它产生了预期的结果。有没有更好或更有效的方法来实现这一目标?该程序每天必须搜索 100,000 个 XML 个文件,每个文件的大小为 300 KB 到 50 MB。
XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
var xLines = xDoc
.Descendants("page")
.Descendants("line");
foreach (var xLine in xLines)
{
var xFirstWords = xLine
.Descendants("word")
.Where(item => item.Value.ToUpper().Contains("HI"));
foreach (var xFirstWord in xFirstWords)
{
var xNextWord = xFirstWord.NodesAfterSelf().OfType<XElement>().First();
if(xNextWord.Value.ToUpper().Contains("BOB"))
{
MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
}
}
}
我可以即兴创作我的代码。如果您有更好的解决方案,请告诉我。
XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
var xLines = xDoc
.Descendants("page")
.Descendants("line");
foreach (var xLine in xLines)
{
var xFirstWords = xLine
.Descendants("word")
.Where(item => item.Value.ToUpper().Contains("HI"))
.Where(item => item.ElementsAfterSelf("word").First().Value.ToUpper().Contains("BOB"));
foreach (var xFirstWord in xFirstWords)
{
var xNextWord = xFirstWord.ElementsAfterSelf("word").First();
MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
}
}
我不知道这段代码的性能会更好还是更差,但我很确定它会有所不同,因此可能值得一试。重构该行的文字,然后用正则表达式匹配。
Regex re = new Regex(@"^.*Hi\s+\S+\s+Bob$*", RegexOptions.IgnoreCase);
XDocument xDoc = XDocument.Load(@"C:\Users\user\Documents\temp.xml");
foreach (XElement xLine in xDoc.Root.Descendants("line")) {
string text = string.Join(" ", xLine.Elements("word").Select(x => x.Value));
if (re.IsMatch(text)) {
Console.WriteLine(text);
}
}
在性能方面想到的事情:
.Nodes
将比.Descendants
更快,因为它只获取直接子项。- 使用
IndexOf
和OrdinalIgnoreCase
而不是ToUpper.Contains
。 - 在
foreach
而不是NodesAfterSelf
中,你可以只持有前一个节点。
var xLines = xDoc.Descendants("line");
foreach (var xLine in xLines)
{
XNode prevWord = null;
foreach (var word in xLine.Nodes("word"))
{
if(prevWord == null && word.Value.IndexOf("HI", StringComparison.OrdinalIgnoreCase))
{
prevWord == word;
}
else if(prevWord != null && word.Value.IndexOf("BOB"), StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show(xLine.FirstAttribute.Value + " " + prevWord.FirstAttribute.Value + " " + word.FirstAttribute.Value);
}
}
}