繁体   English   中英

比较 c# 中两个不同 CSV 文件的行值

[英]compare rows values of two different CSV files in c#

我知道还有更多类似的问题,但我找不到我的答案。 我有两个 CSV 文件。 两个文件都包含相同图像的图像元数据,但是,第一个文件的图像 ID 已过时。 所以我需要从第二个文件中获取 ID,并用新的 ID 替换过时的 ID。 我正在考虑比较图像经度、纬度和高度行值,以及它在两个文件中的匹配位置,我从第二个文件中获取图像 id。 这些 ID 将用于新的 object。 并且文件中的行顺序不同,第一个文件包含的行比第二个文件多。

文件结构如下所示:

第一个文件:

ImgID,Longitude,Latitude,Altitude
01,44.7282372307,27.5786807185,14.1536407471
02,44.7287939869,27.5777060219,13.2340240479
03,44.7254687824,27.582636255,16.5887145996
04,44.7254294913,27.5826908925,16.5794525146
05,44.728785278,27.5777185252,13.2553100586
06,44.7282279311,27.5786933339,14.1576690674
07,44.7253847039,27.5827526969,16.6026000977
08,44.7287777782,27.5777295052,13.2788238525
09,44.7282196988,27.5787045314,14.1649169922
10,44.7253397041,27.5828151049,16.6300048828
11,44.728769439,27.5777417846,13.3072509766

第二个文件:

ImgID,Longitude,Latitude,Altitude
5702,44.7282372307,27.5786807185,14.1536407471
5703,44.7287939869,27.5777060219,13.2340240479
5704,44.7254687824,27.582636255,16.5887145996
5705,44.7254294913,27.5826908925,16.5794525146
5706,44.728785278,27.5777185252,13.2553100586
5707,44.7282279311,27.5786933339,14.1576690674

如何在 C# 中做到这一点? 有一些方便的图书馆可以使用吗?

如果由于某种原因您不想使用 CSVHelper,另一种方法是编写一个方法来比较两行数据并确定它们是否相等(通过忽略第一列数据):

public static bool DataLinesAreEqual(string first, string second)
{
    if (first == null || second == null) return false;
    var xParts = first.Split(',');
    var yParts = second.Split(',');
    if (xParts.Length != 4 || yParts.Length != 4) return false;
    return xParts.Skip(1).SequenceEqual(yParts.Skip(1));
}

然后我们可以将两个文件中的所有行读入 arrays,然后如果我们的方法说它们相等,我们可以用第二个文件中的行更新我们的第一个文件行:

var csvPath1 = @"c:\temp\csvData1.csv";
var csvPath2 = @"c:\temp\csvData2.csv";

// Read lines from both files
var first = File.ReadAllLines(csvPath1);
var second = File.ReadAllLines(csvPath2);

// Select the updated line where necessary
var updated = first.Select(f => second.FirstOrDefault(s => DataLinesAreEqual(f, s)) ?? f);

// Write the updated result back to the first file
File.WriteAllLines(csvPath1, updated);

我会使用CSVHelper进行 CSV 读/写,因为它是一个完整的好库。 为此,您应该声明一个 class 来保存您的数据,并且它的属性名称必须与您的 CSV 文件的列名称匹配。

public class ImageData
{
    public int ImgID { get; set; }
    public double Longitude { get; set; }
    public double Latitude { get; set; }
    public double Altitude { get; set; }
}

然后要查看两行是否相等,您需要做的是查看一个文件中每一行中的每个属性是否与另一个匹配。 您可以通过简单地比较属性来做到这一点,但我宁愿为此编写一个比较器,如下所示:

public class ImageDataComparer : IEqualityComparer<ImageData>
{
    public bool Equals(ImageData x, ImageData y)
    {
        return (x.Altitude == y.Altitude && x.Latitude == y.Latitude && x.Longitude == y.Longitude);
    }

    public int GetHashCode(ImageData obj)
    {
        unchecked
        {
            int hash = (int)2166136261;
            hash = (hash * 16777619) ^ obj.Altitude.GetHashCode();
            hash = (hash * 16777619) ^ obj.Latitude.GetHashCode();
            hash = (hash * 16777619) ^ obj.Longitude.GetHashCode();
            return hash;
        }
    }
}

简单的解释是我们重写了Equals()方法,并规定如果三个属性值匹配,则ImageData class 的两个实例是相等的。 我稍后会展示用法。

CSV 读/写部分非常简单(库的帮助页面有一些很好的示例和提示,请阅读)。 我可以像这样编写两种读写方法:

public static List<ImageData> ReadCSVData(string filePath)
{
    List<ImageData> records;
    using (var reader = new StreamReader(filePath))
    {
        using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
        {
            csv.Configuration.HasHeaderRecord = true;
            records = csv.GetRecords<ImageData>().ToList();
        }
    }
    return records;
}

public static void WriteCSVData(string filePath, List<ImageData> records)
{
    using (var writer = new StreamWriter(filePath))
    {
        using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
        {
            csv.WriteRecords(records);
        }
    }
}

您实际上可以编写通用<T>读/写方法,以便这两种方法可用于不同的类,如果这对您有用的话。

接下来是关键部分。 首先,使用我们刚刚定义的方法将这两个文件读取到 memory。

var oldData = ReadCSVData(Path.Combine(Directory.GetCurrentDirectory(), "OldFile.csv"));
var newData = ReadCSVData(Path.Combine(Directory.GetCurrentDirectory(), "NewFile.csv"));

现在,我可以通过'旧'数据中的每一行go,看看'新'数据中是否有相应的记录。 如果是这样,我从新数据中获取 ID 并用它替换旧数据的 ID。 注意我们编写的比较器的用法。

foreach (var line in oldData)
{
    var replace = newData.FirstOrDefault(x => new ImageDataComparer().Equals(x, line));
    if (replace != null && replace.ImgID != line.ImgID)
    {
        line.ImgID = replace.ImgID;
    }
}

接下来,只需覆盖旧的数据文件。

WriteCSVData(Path.Combine(Directory.GetCurrentDirectory(), "OldFile.csv"), oldData);

结果

我正在使用您的数据的简化版本来轻松验证我们的结果。

旧数据

ImgID,Longitude,Latitude,Altitude
1,1,2,3
2,2,3,4
3,3,4,5
4,4,5,6
5,5,6,7
6,6,7,8
7,7,8,9
8,8,9,10
9,9,10,11
10,10,11,12
11,11,12,13

新数据

ImgID,Longitude,Latitude,Altitude
5702,1,2,3
5703,2,3,4
5704,3,4,5
5705,4,5,6
5706,5,6,7
5707,6,7,8

现在我们的预期结果应该是旧文件的前 6 行应该更新了 id,这就是我们得到的结果:

更新旧数据

ImgID,Longitude,Latitude,Altitude
5702,1,2,3
5703,2,3,4
5704,3,4,5
5705,4,5,6
5706,5,6,7
5707,6,7,8
7,7,8,9
8,8,9,10
9,9,10,11
10,10,11,12
11,11,12,13

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM