Обработка файлов разных форматов C#

Всем привет. Есть класс, который занимается обработкой файлов разного формата (Html, Text, JSON …). Предполагается, что будут добавляться новые форматы файлов. Напишите реализацию, позволяющую расширять форматы обрабатываемых файлов. Детали обработки конкретного содержимого файла неважны.

public class FileProcessor
   { 
       public void ProcessFile(string fileName)
       {
            
       }  

   }

Я думаю нужно использовать шаблон стратегия, тип будет интерфейс IReader с методом readFile(string fileName), но опять же вопрос что он должен возвращять string для всех файлов ? И далее для обработки получается также использовать стратегию, тип на каждый файл своя обработка или считал в string и алгоритм общий для всех ? Или вообще по-другому. Интерфейс IFileProcess? и в нем считывает из своего формата и что то делает ?


Ответы (2 шт):

Автор решения: DanBit

Оцените такое решение, думаю имеет место быть. Использовал шаблонный метод и стратегию

abstract class AbstractFileProcessor
{
    public void ProcessFile(string fileName)
    {
        if (File.Exists(fileName) && Path.GetExtension(fileName).Contains(GetFormat()))
            StartWork(fileName);
        else
            Console.WriteLine("incorrect file format or file not exist");
    }

    protected abstract string GetFormat();
    protected abstract void StartWork(string fileName);
}

class FileProcessor
{
    public AbstractFileProcessor fileProcessor;

    public FileProcessor(AbstractFileProcessor fileProcessor) => this.fileProcessor = fileProcessor;

    public void ProcessFile(string fileName)
    {
        fileProcessor.ProcessFile(fileName);
    }
}

    protected abstract string GetFormat();
    protected abstract void StartWork(string fileName);
}

class HtmlProcess : AbstractFileProcessor
{
    protected override string GetFormat() => ".html";

    protected override void StartWork(string fileName)
    {
        Console.WriteLine("Process with html file");
    }
}

class JsonProcess : AbstractFileProcessor
{
    protected override string GetFormat() => ".json";

    protected override void StartWork(string fileName)
    {
        Console.WriteLine("Process with json file");
    }
}

class TextProcess : AbstractFileProcessor
{
    protected override string GetFormat() => ".txt";

    protected override void StartWork(string fileName)
    {
        Console.WriteLine("Process with txt file");
    }
}

class Program
{
    static void Main(string[] args)
    {
        FileProcessor fp = new FileProcessor(new HtmlProcess());
        fp.ProcessFile(@"C:\Users\danil\source\repos\FileWorker\FileWorker\hello.html");

        fp.fileProcessor = new TextProcess();
        fp.ProcessFile(@"C:\Users\danil\source\repos\FileWorker\FileWorker\hello.txt");

        fp.fileProcessor = new JsonProcess();
        fp.ProcessFile(@"C:\Users\danil\source\repos\FileWorker\FileWorker\hello.json");

        Console.ReadLine();
    }
}
→ Ссылка
Автор решения: aepot

На гениальность не претендую, но попробую предложить свой велосипед с квадратными колесами из разряда "А еще вот так вот можно".

Вся реализация должна исходить из задачи, откуда читаем данные и как читаем, и вот здесь разделение на 2: откуда и как. Пусть представленный класс будет контейнером для данных. Пусть его назначение пережевывать небольшие порции данных, предварительно полностью загружая их в память.

И здесь я разделю класс на статическую часть и часть, которая будет жить в экземпляре. Статическая часть будет отвечать за загрузку данных, а объектная - за их преобразование. И первое что приходит в голову - это хранить массив byte[], прочитали и положили, и что дальше с ними делать - будет решать следующий вызов.

Без абстракций, только рабочая часть. Я просто здесь не нашел места для абстракций, которые бы имели смысл.

public class DataReader
{
    private readonly byte[] _data;

    private DataReader(byte[] data) 
        => _data = data;

    public static DataReader FromArray(byte[] data)
    {
        byte[] buffer = new byte[data.Length];
        data.CopyTo(buffer, 0);
        return new DataReader(buffer); 
    }

    public static DataReader FromString(string text)
        => new DataReader(Encoding.UTF8.GetBytes(text));

    public static DataReader FromFile(string fileName)
        => new DataReader(File.ReadAllBytes(fileName));

    public static DataReader FromStream(Stream stream)
    {
        using MemoryStream ms = new MemoryStream();
        stream.CopyTo(ms);
        return new DataReader(ms.ToArray());
    }

    public T AsJson<T>() 
        => JsonSerializer.Deserialize<T>(_data);

    public string AsString() 
        => Encoding.UTF8.GetString(_data);

    public HtmlDocument AsHtml()
    {
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(AsString());
        return doc;
    }
}

То есть на вход можно подать байты, строку или файл. А на выходе получить HtmlDocument (HtmlAgilityPack), десереализованный из JSON объект или строку.

class Program
{
    static void Main(string[] args)
    {
        string dataString = "{ \"message\": \"ok\" }";

        DataReader reader = DataReader.FromString(dataString);

        MessageData message = reader.AsJson<MessageData>();
        Console.WriteLine($"Message: {message.Message}");
        string text = reader.AsString();
        Console.WriteLine(text);

        Console.ReadKey();
    }
}

public class MessageData
{
    [JsonPropertyName("message")]
    public string Message { get; set; }
}

Вывод в консоль

Message: ok
{ "message": "ok" }

Проверять расширение файла? А зачем, а что потом делать, если расширение не совпало, бросать исключение? А если вдруг потребовалось временный файл прочитать с расширением filename.json.tmp? А если этот JSON вообще не из файла пришел, а из сети по HTTP?

По поводу расширяемости - легко писать новые методы, как для загрузки данных, так и для конвертации, можно обозвать его partial разные части класса хранить в разных .cs файлах. Неизменно только одно - byte[] массив, хранящий данные.

Практическая сфера применения данного класса никакая, он бесполезен. Если мне надо данные откуда-то перегнать куда-то, я просто сяду и напишу код, с буфером, без буфера, с асинхронностью, как угодно, главное - код будет оптимизирован под конкретную задачу. Для меня обобщение такого сорта операций - это попытка сделать универсальным несовместимое, то есть сама идея обречена на провал, но честно попытался, о чем свидетельстует код, опубликованный выше. Можно хоть сотню модных паттернов на это накрутить, суть не поменяется. Потому что всё что нужно для работы с данными - уже есть в .NET в виде более-менее удобных API, которые уже сами по себе являются обертками над чем-то еще более низкоуровневым, и делать обертку над оберткой - это как фабрику фабрик или абстракцию над абстракцией. Я не люблю писать код ради того, чтобы писать код, мне нужен смысл, но я его здесь, к сожалению, не увидел.

→ Ссылка