如何比较2列的两个csv文件?

时间:2016-08-19 19:37:44

标签: c# list compare

我有2个 csv 文件

1.csv

accepts_nested_attributes_for

2.csv

spain;russia;japan
italy;russia;france

我读了两个文件并将数据添加到列表中

spain;russia;japan
india;iran;pakistan

然后我找到两个列表中的所有唯一字符串并将其添加到结果列表

var lst1= File.ReadAllLines("1.csv").ToList();
var lst2= File.ReadAllLines("2.csv").ToList();

rezlist包含此数据

var rezList = lst1.Except(lst2).Union(lst2.Except(lst1)).ToList();

现在我想在所有行中通过第二第三列进行比较,制作除外和并用。

1.csv

西班牙;的俄罗斯;日本

意大利;的俄罗斯;法国

2.csv

西班牙;的俄罗斯;日本

印度;的伊朗;巴基斯坦

我想我需要按符号';'拆分所有行并进行所有 3 操作(不同联盟),但无法理解。

[0] = "italy;russia;france" [1] = "india;iran;pakistan" 必须包含

rezlist

我添加了课程

india;iran;pakistan

4 个答案:

答案 0 :(得分:4)

您的问题不是很清楚:例如,india;iran;pakistan所需的结果主要是因为俄罗斯位于元素[1]?还包括因为元素[2] 巴基斯坦法国日本不匹配?即使这些不清楚,我认为所期望的结果来自任何一种情况。

然后就是: find all unique string from both lists 会大大改变这种性质。所以,我认为期望的结果是因为"伊朗"出现在任一文件的第[1]栏中的列[1]中的其他位置,即使它确实存在,由于"巴基斯坦"该行仍然是唯一的。在col [2]。

另请注意,2的数据样本会留下相当大的错误空间。

尝试一步完成就会让人非常困惑。由于消除1.CSV中的欺骗很容易,首先要做:

// parse "1.CSV"
List<string[]> lst1 = File.ReadAllLines(@"C:\Temp\1.csv").
            Select(line => line.Split(';')).
            ToList();

// parse "2.CSV"
List<string[]> lst2 = File.ReadAllLines(@"C:\Temp\2.csv").
            Select(line => line.Split(';')).
            ToList();

// extracting once speeds things up in the next step
//  and leaves open the possibility of iterating in a method
List<List<string>> tgts = new List<List<string>>();
tgts.Add(lst1.Select(z => z[1]).Distinct().ToList());
tgts.Add(lst1.Select(z => z[2]).Distinct().ToList());

var tmpLst = lst2.Where(x => !tgts[0].Contains(x[1]) ||
                !tgts[1].Contains(x[2])).
                ToList();

这导致不在1.CSV中的项目(Col [1]和Col [2]中没有匹配的文本)。如果这真的是你所需要的,那么你就完成了。

在2.CSV中获取唯一行是比较棘手的,因为你必须实际计算每个Col [1]项目出现的次数,看它是否是唯一的;然后重复Col [2]。这使用GroupBy:

var unique = tmpLst.
    GroupBy(g => g[1], (key, values) =>
            new GroupItem(key, 
                    values.ToArray()[0], 
                    values.Count())
            ).Where(q => q.Count == 1).
    GroupBy(g => g.Data[2], (key, values) => new
            {
                Item = string.Join(";", values.ToArray()[0]),
                Count = values.Count()
            }
            ).Where(q => q.Count == 1).Select(s => s.Item).
        ToList();

GroupItem课程很简单:

class GroupItem
{
    public string Item { set; get; }      // debug aide
    public string[] Data { set; get; }
    public int Count { set; get; }

    public GroupItem(string n, string[] d, int c)
    {
        Item = n;
        Data =  d;
        Count = c;
    }
    public override string ToString()
    {
        return string.Join(";", Data);
    }
}

tmpList开头,在[1]处获取具有唯一元素的行。它使用一个类进行存储,因为此时我们需要数组数据以供进一步检查。

第二个GroupBy对这些结果起作用,这次是看col [2]。最后,它选择连接的字符串数据。

结果

在File1(1.3 MB)中使用50,000个随机项,在File2(390 kb)中使用15,000个随机项。没有自然发生的独特项目,所以我在2.CSV中手动制作了8个唯一项目,并将其中2个复制到1.CSV中。如果2.CSV中的8个唯一行使预期结果成为6个唯一行,则1.CSV中的副本应消除2:

enter image description here

NepalXItalyX是两个文件中的重复内容,它们正确地相互消除。

每一步都是扫描并使用越来越少的数据,这似乎使得65,000行/ 130,000个数据元素的速度非常快。

答案 1 :(得分:0)

你的GetHashCode() - EqualityComparer中的方法有问题。修正版:

public int GetHashCode(string obj)
{
  return obj.Split(';')[1].GetHashCode();
}

现在结果是正确的:

// one result: "india;iran;pakistan"

顺便说一句。 "StringLengthEqualityComparer"不是一个好名字; - )

答案 2 :(得分:0)

private void GetUnion(List<string> lst1, List<string> lst2)
{
    List<string> lstUnion = new List<string>();
    foreach (string value in lst1)
    {
        string valueColumn1 = value.Split(';')[0];
        string valueColumn2 = value.Split(';')[1];
        string valueColumn3 = value.Split(';')[2];

        string result = lst2.FirstOrDefault(s => s.Contains(";" + valueColumn2 + ";" + valueColumn3));

        if (result != null)
        {
            if (!lstUnion.Contains(result))
            {
                lstUnion.Add(result);
            }                   
        }
    }
}

答案 3 :(得分:0)

  class Program
    {
        static void Main(string[] args)
        {
            var lst1 = File.ReadLines(@"D:\test\1.csv").Select(x => new StringWrapper(x)).ToList();
            var lst2 = File.ReadLines(@"D:\test\2.csv").Select(x => new StringWrapper(x));
            var set = new HashSet<StringWrapper>(lst1);
            set.SymmetricExceptWith(lst2);

            foreach (var x in set)
            {
                Console.WriteLine(x.Value);
            }

        }
    }

    struct StringWrapper : IEquatable<StringWrapper>
    {
        public string Value { get; }
        private readonly string _comparand0;
        private readonly string _comparand14;

        public StringWrapper(string value)
        {
            Value = value;
            var split = value.Split(';');
            _comparand0 = split[0];
            _comparand14 = split[14];
        }

        public bool Equals(StringWrapper other)
        {
            return string.Equals(_comparand0, other._comparand0, StringComparison.OrdinalIgnoreCase)
                   && string.Equals(_comparand14, other._comparand14, StringComparison.OrdinalIgnoreCase);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            return obj is StringWrapper && Equals((StringWrapper) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((_comparand0 != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_comparand0) : 0)*397)
                       ^ (_comparand14 != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_comparand14) : 0);
            }
        }
    }