簡體   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