需要帮助以递增的顺序查找特定节点?

时间:2018-03-07 15:58:22

标签: c# linq linq-to-xml

我有一个以下方式的字符串数组(每个元素包含至少3个名称为xref的节点,其属性为ref-typerid

<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 filefull code我正在使用

我试图在某些xml文件中找到一些连续的节点<xref ref-type="bibr" rid="ref...">...</xref>(当 3 或更多时),这些文件由逗号分隔或逗号和空格并将其写入日志文件。我正在尝试识别的连续节点应该使其各自的属性rid值增加+1减去文本ref。除了xref之外,不需要检查具有不同rid值的任何其他refX个节点。

3 个答案:

答案 0 :(得分:1)

我不认为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并扫描收集顺序编号元素的元素。收集它们之后,如果找到至少三个连续元素,则可以输出所有元素和节点。

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", ""));
}

答案 1 :(得分:0)

我把它作为一个单独的答案,因为我不确定我认为它更好还是更清楚,但是可以使用LINQ通过分组来查找序列。

我创建了Scan扩展名的变体,它是APL扫描运算符的一种实现,类似于Aggregate,但它将中间结果作为序列返回。

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());
}

答案 2 :(得分:-2)

你的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>