需要帮助查找递增序列中的特定节点?
Need help finding specific nodes in a incremented sequence?
我有一个字符串数组,如下所示(每个元素至少包含 3 个名称为 xref
且属性为 ref-type
和 rid
的节点)
<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref>, <xref ref-type="bibr" rid="ref104">104</xref>, <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
我正在尝试遍历数组的每个元素并找到 3 个或更多节点 xref
,它们各自的 rid
属性值增加 +1,不包括文本 rid
并将它们输出到控制台或文件。
我完成了
foreach (var element in xrefs)
{
XDocument xd = XDocument.Parse("<root>"+element+"</root>",LoadOptions.SetLineInfo);
int len = xd.Descendants("xref").Count();
var values = from El in xd.Descendants("xref").Take(len - 2)
where El.CompareNext() && El.ElementsAfterSelf().FirstOrDefault().CompareNext()
select El;
foreach (var value in values)
{
Console.WriteLine(new string('-',50)+"\r\n"+element+"\r\n");
}
}
其中xrefs
是数组,ElementsAfterSelf()
是如下创建的方法
static class T1
{
public static Boolean CompareNext(this XElement xe)
{
return Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(xe.ElementsAfterSelf().FirstOrDefault().Attribute("rid").Value.Replace("ref", ""));
}
}
现在它产生的结果就像
--------------------------------------------------
<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
下面的字符串写了两次,但我只想要一次,因为它是同一件事
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
有人能帮忙吗?
这是我正在使用的sample xml file and the full code
我试图在某些 xml 文件中找到一些连续的节点 <xref ref-type="bibr" rid="ref...">...</xref>
(当有 3 或更多时) 一个逗号或一个逗号和一个space并将它们写入日志文件。我试图识别的连续节点应该有它们各自的属性 rid
值递增 +1 减去文本 ref
。除了 refX
之外,任何其他具有不同 rid
值的 xref
节点都不需要检查。
您的 xml 是一个元素数组,所以我不明白您要做什么。
<Root>
<xref ref-type="bibr" rid="ref20">[20]</xref>
<xref ref-type="bibr" rid="ref21">[21]</xref>
<xref ref-type="bibr" rid="ref22">[22]</xref>
<xref ref-type="bibr" rid="ref2">[2]</xref>
<xref ref-type="bibr" rid="ref3">[3]</xref>
<xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref101">101</xref>
<xref ref-type="bibr" rid="ref102">102</xref>
<xref ref-type="bibr" rid="ref103">103</xref>
<xref ref-type="bibr" rid="ref104">104</xref>
<xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>
<xref ref-type="bibr" rid="ref12">[12]</xref>
<xref ref-type="bibr" rid="ref13">[13]</xref>
<xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>
<xref ref-type="bibr" rid="ref12">[12]</xref>
<xref ref-type="bibr" rid="ref13">[13]</xref>
<xref ref-type="bibr" rid="ref14">[14]</xref>
</Root>
我认为 LINQ 版本不会显着改善代码或理解。
既然要输出元素之间的文字,可以写个辅助函数输出两个节点之间的XNode
:
var dashes = new String('-', 50);
void WriteNodesBetween(XNode from, XNode to) {
Console.WriteLine(dashes);
var xn = from;
for (; xn != to; xn = xn.NextNode)
Console.Write(xn.ToString());
Console.WriteLine(xn.ToString());
}
然后您可以将字符串转换为 XNode
s 并扫描元素,收集按顺序编号的元素。一旦你收集了它们,如果至少找到三个连续元素,你可以输出所有元素和它们之间的节点。
foreach (var element in xrefs) {
var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");
var outElements = new List<XElement>() { xd.First() };
foreach (var el in xd.Skip(1)) {
if (!outElements.Last().ISSequential(el)) {
if (outElements.Count >= 3)
WriteNodesBetween(outElements.First(), outElements.Last());
outElements.Clear();
}
outElements.Add(el);
}
if (outElements.Count >= 3)
WriteNodesBetween(outElements.First(), outElements.Last());
}
这使用另一个助手来测试两个元素是否连续。
public static class Ext {
public static bool ISSequential(this XElement xe, XElement nextxe) => Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(nextxe.Attribute("rid").Value.Replace("ref", ""));
}
我把它作为一个单独的答案,因为我不确定我认为它更好或更清楚,但可以使用 LINQ 通过对序列进行分组来查找序列。
我创建了我的 Scan
扩展的一个变体,它是 APL 扫描运算符的一个实现,类似于 Aggregate
但它 returns 中间结果作为一个序列。
ScanPair
扩展使用 ValueTuple
将中间结果与结果序列中的当前元素相匹配:
public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combine) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combine(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
}
使用 ScanPair
,您可以创建一个扩展以根据谓词进行分组:
public static IEnumerable<IGrouping<int, TRes>> GroupByWhile<T, TRes>(this IEnumerable<T> src, Func<T, T, bool> test, Func<T, TRes> result) =>
src.ScanPair(1, (kvp, cur) => test(kvp.Value, cur) ? kvp.Key : kvp.Key+1)
.GroupBy(kvp => kvp.Key, kvp => result(kvp.Value));
public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> test) => src.GroupByWhile(test, e => e);
使用 GroupByWhile
您可以创建一个扩展以按顺序值分组:
public static IEnumerable<IGrouping<int, TRes>> GroupBySequential<T, TRes>(this IEnumerable<T> src, Func<T, int> SeqNum, Func<T, TRes> result) => src.GroupByWhile((prev,cur) => SeqNum(prev)+1 == SeqNum(cur), result);
public static IEnumerable<IGrouping<int, T>> GroupBySequential<T>(this IEnumerable<T> src, Func<T, int> SeqNum) => src.GroupBySequential(SeqNum, e => e);
现在 GroupBySequential
可用,您可以从每个字符串中提取序列:
var dashes = new String('-', 50);
void WriteNodesBetween(XNode from, XNode to) {
Console.WriteLine(dashes);
var xn = from;
for (; xn != to; xn = xn.NextNode)
Console.Write(xn.ToString());
Console.WriteLine(xn.ToString());
}
foreach (var element in xrefs) {
var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");
var refseqs = xd.GroupBySequential(xref => xref.RefValue().Value);
foreach (var seq in refseqs.Where(sg => sg.Count() >= 3))
WriteNodesBetween(seq.First(), seq.Last());
}
我有一个字符串数组,如下所示(每个元素至少包含 3 个名称为 xref
且属性为 ref-type
和 rid
的节点)
<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref>, <xref ref-type="bibr" rid="ref104">104</xref>, <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
我正在尝试遍历数组的每个元素并找到 3 个或更多节点 xref
,它们各自的 rid
属性值增加 +1,不包括文本 rid
并将它们输出到控制台或文件。
我完成了
foreach (var element in xrefs)
{
XDocument xd = XDocument.Parse("<root>"+element+"</root>",LoadOptions.SetLineInfo);
int len = xd.Descendants("xref").Count();
var values = from El in xd.Descendants("xref").Take(len - 2)
where El.CompareNext() && El.ElementsAfterSelf().FirstOrDefault().CompareNext()
select El;
foreach (var value in values)
{
Console.WriteLine(new string('-',50)+"\r\n"+element+"\r\n");
}
}
其中xrefs
是数组,ElementsAfterSelf()
是如下创建的方法
static class T1
{
public static Boolean CompareNext(this XElement xe)
{
return Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(xe.ElementsAfterSelf().FirstOrDefault().Attribute("rid").Value.Replace("ref", ""));
}
}
现在它产生的结果就像
--------------------------------------------------
<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
下面的字符串写了两次,但我只想要一次,因为它是同一件事
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>
有人能帮忙吗?
这是我正在使用的sample xml file and the full code
我试图在某些 xml 文件中找到一些连续的节点 <xref ref-type="bibr" rid="ref...">...</xref>
(当有 3 或更多时) 一个逗号或一个逗号和一个space并将它们写入日志文件。我试图识别的连续节点应该有它们各自的属性 rid
值递增 +1 减去文本 ref
。除了 refX
之外,任何其他具有不同 rid
值的 xref
节点都不需要检查。
您的 xml 是一个元素数组,所以我不明白您要做什么。
<Root>
<xref ref-type="bibr" rid="ref20">[20]</xref>
<xref ref-type="bibr" rid="ref21">[21]</xref>
<xref ref-type="bibr" rid="ref22">[22]</xref>
<xref ref-type="bibr" rid="ref2">[2]</xref>
<xref ref-type="bibr" rid="ref3">[3]</xref>
<xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref101">101</xref>
<xref ref-type="bibr" rid="ref102">102</xref>
<xref ref-type="bibr" rid="ref103">103</xref>
<xref ref-type="bibr" rid="ref104">104</xref>
<xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>
<xref ref-type="bibr" rid="ref12">[12]</xref>
<xref ref-type="bibr" rid="ref13">[13]</xref>
<xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>
<xref ref-type="bibr" rid="ref12">[12]</xref>
<xref ref-type="bibr" rid="ref13">[13]</xref>
<xref ref-type="bibr" rid="ref14">[14]</xref>
</Root>
我认为 LINQ 版本不会显着改善代码或理解。
既然要输出元素之间的文字,可以写个辅助函数输出两个节点之间的XNode
:
var dashes = new String('-', 50);
void WriteNodesBetween(XNode from, XNode to) {
Console.WriteLine(dashes);
var xn = from;
for (; xn != to; xn = xn.NextNode)
Console.Write(xn.ToString());
Console.WriteLine(xn.ToString());
}
然后您可以将字符串转换为 XNode
s 并扫描元素,收集按顺序编号的元素。一旦你收集了它们,如果至少找到三个连续元素,你可以输出所有元素和它们之间的节点。
foreach (var element in xrefs) {
var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");
var outElements = new List<XElement>() { xd.First() };
foreach (var el in xd.Skip(1)) {
if (!outElements.Last().ISSequential(el)) {
if (outElements.Count >= 3)
WriteNodesBetween(outElements.First(), outElements.Last());
outElements.Clear();
}
outElements.Add(el);
}
if (outElements.Count >= 3)
WriteNodesBetween(outElements.First(), outElements.Last());
}
这使用另一个助手来测试两个元素是否连续。
public static class Ext {
public static bool ISSequential(this XElement xe, XElement nextxe) => Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(nextxe.Attribute("rid").Value.Replace("ref", ""));
}
我把它作为一个单独的答案,因为我不确定我认为它更好或更清楚,但可以使用 LINQ 通过对序列进行分组来查找序列。
我创建了我的 Scan
扩展的一个变体,它是 APL 扫描运算符的一个实现,类似于 Aggregate
但它 returns 中间结果作为一个序列。
ScanPair
扩展使用 ValueTuple
将中间结果与结果序列中的当前元素相匹配:
public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combine) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combine(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
}
使用 ScanPair
,您可以创建一个扩展以根据谓词进行分组:
public static IEnumerable<IGrouping<int, TRes>> GroupByWhile<T, TRes>(this IEnumerable<T> src, Func<T, T, bool> test, Func<T, TRes> result) =>
src.ScanPair(1, (kvp, cur) => test(kvp.Value, cur) ? kvp.Key : kvp.Key+1)
.GroupBy(kvp => kvp.Key, kvp => result(kvp.Value));
public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> test) => src.GroupByWhile(test, e => e);
使用 GroupByWhile
您可以创建一个扩展以按顺序值分组:
public static IEnumerable<IGrouping<int, TRes>> GroupBySequential<T, TRes>(this IEnumerable<T> src, Func<T, int> SeqNum, Func<T, TRes> result) => src.GroupByWhile((prev,cur) => SeqNum(prev)+1 == SeqNum(cur), result);
public static IEnumerable<IGrouping<int, T>> GroupBySequential<T>(this IEnumerable<T> src, Func<T, int> SeqNum) => src.GroupBySequential(SeqNum, e => e);
现在 GroupBySequential
可用,您可以从每个字符串中提取序列:
var dashes = new String('-', 50);
void WriteNodesBetween(XNode from, XNode to) {
Console.WriteLine(dashes);
var xn = from;
for (; xn != to; xn = xn.NextNode)
Console.Write(xn.ToString());
Console.WriteLine(xn.ToString());
}
foreach (var element in xrefs) {
var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");
var refseqs = xd.GroupBySequential(xref => xref.RefValue().Value);
foreach (var seq in refseqs.Where(sg => sg.Count() >= 3))
WriteNodesBetween(seq.First(), seq.Last());
}