Сравните текстовые файлы в C# и удалите повторяющиеся строки

1.txt:

Отправление, пункт назначения, дата и время, цена

YYZ,YTC,2016-04-01 12:30,$550
YYZ,YTC,2016-04-01 12:30,$550
LKC,LKP,2016-04-01 12:30,$550

2.txt:

Отправление|назначение|дата/время|цена

YYZ|YTC|2016-04-01 12:30|$550
AMV|YRk|2016-06-01 12:30|$630
LKC|LKP|2016-12-01 12:30|$990

У меня есть два текстовых файла с ',' и '|' в качестве разделителей, и я хочу создать консольное приложение на С#, которое читает эти два файла, когда я передаю исходное и целевое расположение из командной строки.

При поиске я хочу игнорировать повторяющиеся строки и отображать результаты по цене.

Результат должен быть { origination } -> { destination } -> datetime -> price

Нужна помощь как выполнить.


person cshah    schedule 22.06.2017    source источник
comment
У вас есть пример кода того, что у вас есть?   -  person Gareth    schedule 22.06.2017
comment
Вы умеете читать текстовые файлы? Кроме того, если вы создаете консольное приложение, почему вы пометили его asp.net?   -  person Matt Hogan-Jones    schedule 22.06.2017
comment
Научитесь читать файлы построчно. Узнайте, как разбивать строки по заданному разделителю: ',' или '|' и т.д. Узнайте, как сравнивать строки. Узнайте, как объединять строки и переменные с помощью интерполяции строк, используя знак доллара $ и скобки {} для переменных. Прочитайте оба файла построчно. Разделите и сравните строки, если они не совпадают, добавьте коллекцию с обеими или только с одной из них в зависимости от того, совпадают ли они или нет, создавая новые строки, которые вы хотите использовать с помощью интерполяции строк. Если вы уже знаете это, покажите нам несколько примеров кода, чтобы мы могли помочь вам их улучшить.   -  person Kalin Krastev    schedule 22.06.2017
comment
пока я читаю путь к файлам txt, которые находятся внутри моего проекта, используя ниже System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); он дает мне неправильный путь, он показывает путь к папке отладки, моих файлов здесь нет.   -  person cshah    schedule 22.06.2017


Ответы (2)


Вот простое решение, которое работает для ваших файлов примеров. У него нет проверки ошибок, если файл имеет неправильный формат.

using System;
using System.Collections.Generic;

class Program
{
    class entry
    {
        public string origin;
        public string destination;
        public DateTime time;
        public double price;
    }

    static void Main(string[] args)
    {
        List<entry> data = new List<entry>();

        //parse the input files and add the data to a list
        ParseFile(data, args[0], ',');
        ParseFile(data, args[1], '|');

        //sort the list (by price first)
        data.Sort((a, b) =>
        {
            if (a.price != b.price)
                return a.price > b.price ? 1 : -1;
            else if (a.origin != b.origin)
                return string.Compare(a.origin, b.origin);
            else if (a.destination != b.destination)
                return string.Compare(a.destination, b.destination);
            else
                return DateTime.Compare(a.time, b.time);
        });

        //remove duplicates (list must be sorted for this to work)
        int i = 1;
        while (i < data.Count)
        {
            if (data[i].origin == data[i - 1].origin
                && data[i].destination == data[i - 1].destination
                && data[i].time == data[i - 1].time
                && data[i].price == data[i - 1].price)
                data.RemoveAt(i);
            else
                i++;
        }

        //print the results
        for (i = 0; i < data.Count; i++)
            Console.WriteLine("{0}->{1}->{2:yyyy-MM-dd HH:mm}->${3}",
                data[i].origin, data[i].destination, data[i].time, data[i].price);

        Console.ReadLine();
    }

    private static void ParseFile(List<entry> data, string filename, char separator)
    {
        using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open))
        using (System.IO.StreamReader reader = new System.IO.StreamReader(fs))
            while (!reader.EndOfStream)
            {
                string[] line = reader.ReadLine().Split(separator);
                if (line.Length == 4)
                {
                    entry newitem = new entry();
                    newitem.origin = line[0];
                    newitem.destination = line[1];
                    newitem.time = DateTime.Parse(line[2]);
                    newitem.price = double.Parse(line[3].Substring(line[3].IndexOf('$') + 1));
                    data.Add(newitem);
                }
            }
    }
}
person Ben J    schedule 22.06.2017
comment
Спасибо, позвольте мне попробовать еще один вопрос в командной строке, когда пользователь добавит «$ search -o YYZ -d YYC», и на основе этого мне нужно проверить весь процесс и отобразить результат, как с этим справиться? также в моем текстовом файле у меня есть статический заголовок над данными, как пропустить синтаксический анализ этих заголовков? заранее спасибо - person cshah; 22.06.2017
comment
у меня есть одна вещь, как пропустить - reader.ReadLine().Skip(1); но другая командная строка все еще находится на рассмотрении - person cshah; 22.06.2017

Я не на 100% понимаю, каким должен быть результат вашей программы, поэтому я оставлю эту часть реализации на ваше усмотрение. Моя стратегия состояла в том, чтобы использовать метод конструктора, который принимает строку (которую вы будете читать из файла) и разделитель (поскольку он варьируется) и использовать его для создания объектов, которыми вы можете манипулировать (например, добавлять в наборы хэшей и т. д.).

PriceObject.cs

using System;
using System.Globalization;

namespace ConsoleApplication1
{
class PriceObject
{
    public string origination { get; set; }
    public string destination { get; set; }
    public DateTime time { get; set; }
    public decimal price { get; set; }



    public PriceObject(string inputLine, char delimiter)
    {
        string[] parsed = inputLine.Split(new char[] { delimiter }, 4);
        origination = parsed[0];
        destination = parsed[1];
        time = DateTime.ParseExact(parsed[2], "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
        price = Decimal.Parse(parsed[3], NumberStyles.Currency, new CultureInfo("en-US"));
    }


    public override bool Equals(object obj)
    {
        var item = obj as PriceObject;
        return origination.Equals(item.origination) &&
            destination.Equals(item.destination) &&
            time.Equals(item.time) &&
            price.Equals(item.price);
    }

    public override int GetHashCode()
    {

        unchecked
        {
            var result = 17;
            result = (result * 23) + origination.GetHashCode();
            result = (result * 23) + destination.GetHashCode();
            result = (result * 23) + time.GetHashCode();
            result = (result * 23) + price.GetHashCode();
            return result;
        }
    }


}
}

Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        HashSet<PriceObject> list1 = new HashSet<PriceObject>();
        HashSet<PriceObject> list2 = new HashSet<PriceObject>();

        using (StreamReader reader = File.OpenText(args[0]))
        {
            string line = reader.ReadLine(); // this will remove the header row

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    continue;
                // add each line to our list
                list1.Add(new PriceObject(line, ','));
            }

        }

        using (StreamReader reader = File.OpenText(args[1]))
        {
            string line = reader.ReadLine(); // this will remove the header row

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    continue;
                // add each line to our list
                list2.Add(new PriceObject(line, '|'));
            }

        }

        // merge the two hash sets, order by price
        list1.UnionWith(list2);
        List<PriceObject> output = list1.ToList();

        output.OrderByDescending(x => x.price).ToList();

        // display output here, e.g. define your own ToString method, etc
        foreach (var item in output)
        {
            Console.WriteLine(item.ToString());
        }

        Console.ReadLine();
    }
}
}
person C. Helling    schedule 22.06.2017
comment
Просто примечание, поскольку, согласно вашим комментариям, у вас возникли проблемы с загрузкой файлов: я запустил указанную выше программу с аргументами командной строки: C:\temp\test1.txt C:\temp\test2.txt - person C. Helling; 22.06.2017
comment
Спасибо, позвольте мне попробовать еще один вопрос в командной строке, когда пользователь добавит «$ search -o YYZ -d YYC», и на основе этого мне нужно проверить весь процесс и отобразить результат, как с этим справиться? также в моем текстовом файле у меня есть статический заголовок над данными, как пропустить синтаксический анализ этих заголовков? заранее спасибо - person cshah; 22.06.2017
comment
Не уверен, что вы подразумеваете под проверкой всего процесса, но если вы пытаетесь найти результаты, которые соответствуют, например. источник YYZ, пункт назначения YTC, вы можете использовать эти параметры как переменные в запросе linq. Например. PriceObject outputObject = output.Where(x => x.origination.Equals("YYZ") && x.destination.Equals("YTC")).FirstOrDefault();. Чтобы пропустить заголовки, я сделал это в начале: reader.ReadLine() перед циклом while очистит заголовки. Я отредактирую комментарий, чтобы сделать это более понятным. - person C. Helling; 22.06.2017
comment
Вы знакомы с запуском консольного приложения из командной строки? Им действительно нужно ввести $search и т. д.? См. здесь: stackoverflow.com/questions/12998415/ Если бы я был вами, я бы просто сохранил источник и пункт назначения как переменные, например string origination = args[2] или что там еще. - person C. Helling; 22.06.2017