Рефакторинг кода парсера URL

227
01 марта 2018, 10:04

Создаю парсер URL на C#. Задача: вывести top-N доменов, top-N путей. Пути к файлам(input output) берутся из консоли, N из консоли в виде опционального флага (как его правильно реализовать?)

Работает, но хотелось бы знать, как написать лучше с вашей точки зрения. И флаг, да, его тоже!

class Program
    {
        static void Main(string[] args)
        {
            int N = 0;
            if (args[0] == "-n")
                if (!Int32.TryParse(args[1], out N))
                    throw new FormatException("N is not valid");
            string input = File.ReadAllText(args[2]);
            string pattern = @"(http://|https://)(?<domen>[\da-z\.-]+)/(?<path>[[\/\w \.-]*)";
            Regex regex = new Regex(pattern, RegexOptions.Multiline | RegexOptions.Compiled);
            MatchCollection matchCollection = regex.Matches(input);
            SortedDictionary<string, int> Domens = new SortedDictionary<string, int>();
            SortedDictionary<string, int> Paths = new SortedDictionary<string, int>();
            for (int i = 0; i < matchCollection.Count; i++)
            {
                if (Domens.ContainsKey(matchCollection[i].Groups["domen"].ToString()))
                    (Domens[matchCollection[i].Groups["domen"].ToString()])++;
                else
                    Domens.Add(matchCollection[i].Groups["domen"].ToString(), 1);
                if (Paths.ContainsKey(matchCollection[i].Groups["path"].ToString()))
                    (Paths[matchCollection[i].Groups["path"].ToString()])++;
                else
                    Paths.Add(matchCollection[i].Groups["path"].ToString(), 1);
            }
            //Domen и Path - 2 класса, содержащих поля: строку и частоту встречаемости, реализованы в другом файле
            List<Domen> SortedDomens = new List<Domen>();
            foreach (KeyValuePair<string, int> keyValue in Domens)
            {
                SortedDomens.Add(new logparser.Domen(keyValue.Key, keyValue.Value));
            }
            SortedDomens.Sort();
            List<Path> SortedPaths = new List<Path>();
            foreach (KeyValuePair<string, int> keyValue in Paths)
            {
                SortedPaths.Add(new logparser.Path(keyValue.Key, keyValue.Value));
            }
            SortedPaths.Sort();
            //Вывод сначала общей информации, затем через статический шаблонный класс информацию о доменах и путях
            //Внутри переопределен метод .ToString() в классах Domen и Path
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(args[3], true))
            {
                file.WriteLine("total URLs: {0}, domains: {1}, paths: {2}", 
                                matchCollection.Count + SortedDomens.Count + SortedPaths.Count);
                file.Close();
            }
            WriteToFile<Domen>.writetofile(SortedDomens, args[3], N);
            WriteToFile<Path>.writetofile(SortedPaths, args[3], N);
        }
    }
Answer 1

Если взять готовый пакет для разбора аргументов (PowerArgs), то примерно так:

using PowerArgs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApp30
{
    public class Arguments
    {
        [ArgShortcut("n")]
        public int Top { get; set; }
        [ArgPosition(0)]
        [ArgRequired(PromptIfMissing = true)]
        public string InputFileName { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var arguments = Args.Parse<Arguments>(args);
            var urls = File.ReadAllLines(arguments.InputFileName)
                .Select(l => new Uri(l))
                .ToList();
            PrintTopN(urls, "Host", u => u.Host, arguments.Top);
            PrintTopN(urls, "Path", u => u.AbsolutePath, arguments.Top);
        }
        private static void PrintTopN(List<Uri> urls, string byText, Func<Uri, string> selector, int top)
        {
            Console.WriteLine("By " + byText);
            var groups = urls
                .GroupBy(selector)
                .OrderByDescending(g => g.Count())
                .Select(g => g.Key);
            if (top > 0)
            {
                groups = groups.Take(top);
            }
            // File.WriteAllLines, если нужно вывести в файл
            foreach (var g in groups)
            {
                Console.WriteLine(g);
            }
        }
    }
}
READ ALSO
Расстояние между элементами ListBox

Расстояние между элементами ListBox

Желаемый результат выглядит так:

331
сравнение наименований в справочниках

сравнение наименований в справочниках

доброго времени сутоксвалилась на меня интересная задача, но не знаю с какой стороны к ней подступиться

185
C#. SQL Compact. Страницы индексации

C#. SQL Compact. Страницы индексации

Всем доброго времени сутокТак сложилось что в качестве СУБД использую SQL Server Compact

237
Сжатие файла при сохранении

Сжатие файла при сохранении

Делаю с помощью Selenium скриншот, обрезаю его и хочу сохранить со сжатием, но оно не происходит( - в чём загвоздка ?

203