сравнить значения строк двух разных файлов CSV в c# - PullRequest
0 голосов
/ 06 августа 2020

Я знаю, что есть и другие похожие вопросы, но мне не удалось найти ответ на свой. У меня есть два файла CSV. Оба файла содержат метаданные для одних и тех же изображений, однако идентификаторы изображений первого файла устарели. Поэтому мне нужно взять идентификаторы из второго файла и заменить устаревшие идентификаторы новыми. Я думал сравнить значения строк долготы, широты и высоты изображения, и там, где они совпадают в обоих файлах, я беру идентификатор изображения из второго файла. Идентификаторы будут использоваться в новом объекте. Причем последовательность строк в файлах другая, и первый файл содержит больше строк, чем второй.

Структура файлов выглядит следующим образом:

Первый файл:

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#? Есть ли какая-нибудь удобная библиотека для работы?

Ответы [ 2 ]

2 голосов
/ 06 августа 2020

Альтернативный способ сделать это, если по какой-то причине вы не хотите использовать 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));
}

Затем мы можем прочитать все строки из обоих файлов в массивы, а затем мы можем обновить наши строки первого файла строками из второго файла, если наш метод говорит, что они равны:

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);
1 голос
/ 06 августа 2020

Я бы использовал CSVHelper библиотеку для чтения / записи CSV, поскольку это полноценная хорошая библиотека. Для этого вы должны объявить класс для хранения ваших данных, и его имена свойств должны совпадать с именами столбцов вашего 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);
        }
    }
}

Фактически вы можете написать общие c <T> методы чтения / записи, чтобы эти два метода можно было использовать с разными классами, если это что-то полезно для вас.

Следующая важная часть. Сначала прочтите два файла в память, используя методы, которые мы только что определили.

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

Теперь я могу go просмотреть каждую строку в «старых» данных и посмотреть, есть ли соответствующая запись в ' новые 'данные. Если это так, я беру идентификатор из новых данных и заменяю им идентификатор старых данных. Обратите внимание на использование написанного нами компаратора.

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 строк старых файлов должны иметь обновленные идентификаторы, и вот что мы получим:

Обновленные старые данные

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
...