【问题标题】:how to merge two csv files with different columns and rows in c#如何在c#中合并具有不同列和行的两个csv文件
【发布时间】:2020-01-03 09:10:44
【问题描述】:

我正在尝试合并两个具有不同标题和不同行数/行数的 csv 文件。 使用以下代码,但没有得到正确的输出。它在行相同时起作用。

var first = File.ReadAllLines("firstfile.csv");
var second = File.ReadAllLines("secondfile.csv");
var result = first.Zip(second, (f, s) => string.Join(",", f, s));
File.WriteAllLines("combined.csv", result);

例如: 第一个文件是

col1,colb,colc
a,b,c
a,v,f

第二个文件是

colx,coly
x,y
cc,aa
bb,vv
m,n

输出是get

col1,colb,colc,colx,coly
a,b,c,x,y
a,v,f,cc,aa

第二个文件行丢失。 我的预期输出是

col1,colb,colc,colx,coly
a,b,c,x,y
a,v,f,cc,aa
,,,bb,vv
,,,m,n

【问题讨论】:

  • 如果你有不同的标题,那么“正确”的输出是什么?
  • 如何计算出 file1 的哪一行与 file2 的哪一行
  • 如果我正确假设您的最终目标是什么,您必须为“较短”文件中缺少的行提供数据,为此您需要计算其中的列数。将您的代码重写为普通的旧 for 循环,您可能会知道下一步该做什么(或者至少知道一个更具体的问题的想法)。
  • 您能否添加一个额外的步骤来检查每个文件中的行,对于行数较少的文件,附加空白条目。这样,当您合并时,它们将正确结婚。我会说当前合并正在命中“空”行并且正在停止,或者这会将最终文件中的整个合并行归零。
  • Enumerable.Zip 合并序列,直到它到达其中一个的末尾并忽略额外的行。您可以阅读更多内容documentation

标签: c# .net csv .net-core


【解决方案1】:

没有允许您合并两个长度不等的列表的内置方法。 Zip 只合并到最短的长度。但是,您可以通过修改 Marc Gravell 的出色答案 here 来实现您想要的,以便允许使用默认值。为自己创建一个扩展类,如下所示:

public static class Extensions
{
    public static IEnumerable<T> Merge<T>(this IEnumerable<T> first,
    IEnumerable<T> second, T defaultValue, Func<T, T, T> operation)
    {
        using (var iter1 = first.GetEnumerator())
        using (var iter2 = second.GetEnumerator())
        {
            while (iter1.MoveNext())
            {
                if (iter2.MoveNext())
                {
                    yield return operation(iter1.Current, iter2.Current);
                }
                else
                {
                    yield return operation(iter1.Current, defaultValue);
                }
            }
            while (iter2.MoveNext())
            {
                yield return operation(defaultValue, iter2.Current);
            }
        }
    }
}

您现在可以使用如下代码调用它:

char separator = ',';
var first = File.ReadAllLines("firstfile.csv").AsEnumerable();
var second = File.ReadAllLines("secondfile.csv").AsEnumerable();

string defaultValue = "";
int cnt = 0;
if (first.Count() < second.Count())
{
    cnt = first.FirstOrDefault().Split(separator).Length;
}
else
{
    cnt = second.FirstOrDefault().Split(separator).Length;
}
defaultValue = defaultValue.PadLeft(cnt - 1, separator);
var result = first.Merge(second, defaultValue, (f, s) => string.Join(separator.ToString(), f, s));
File.WriteAllLines("combined.csv", result);

请注意,我添加了一个 char 分隔符并将 ReadAllLines 的结果更改为提供 IEnumerable&lt;string&gt; 而不是 string[] 以使代码更通用。上面的代码还假设这两个文件具有内部一致的列数。

【讨论】:

  • 你先生,是个英雄!
【解决方案2】:

首先,您需要找出两个列表中的哪一个较大,以便循环遍历该列表,一旦超过较小列表的长度,您就可以用空值填充缺失的单元格。

接下来,您需要知道较小的列表中有多少列,因为您想用空值填充这些列。这意味着您必须获取较小列表的标题行,用逗号分隔并计算列数。

然后生成一个包含您的空单元格的字符串(例如,如果您的较小列表有 3 列,您需要一个字符串 ",," - String Padding 可能会有所帮助)。

因此,您只需遍历较大的列表并获取两个相应的行(或使用您之前生成的空行)并用逗号连接它们并将它们放入一个列表中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-31
    • 2021-10-19
    • 2021-10-04
    • 2016-03-20
    • 2018-03-13
    • 1970-01-01
    • 2021-05-11
    • 1970-01-01
    相关资源
    最近更新 更多