Слияние абзацев с удалением перекрывающихся повторяющихся строк в C #

Я пытаюсь сделать слияние абзацев, которое берет несколько абзацев и выводит объединенный результат с удалением дублированных перекрывающихся строк из-за избыточности. Каждый входной абзац имеет следующие спецификации.

  • Начальные / конечные пробелы в каждой строке были удалены.

  • Нет пустой строки.

Выходной объединенный абзац следует правилам, указанным ниже.

  • Ввод абзаца в Update объединяется после предыдущего ввода.

  • Если строка (строки) от начала ввода абзаца до Update Этот метод упорядочен так же (перекрывается), как и конец предыдущего ввода, просто сохраните одну копию упорядоченных повторяющихся строк.

  • Определение повторяющихся строк здесь:

    • Содержание в двух строках должно быть полностью одинаковым, не нужно рассматривать случаи «частичного перекрытия».

    • Последовательность содержимого в двух блоках строк должна быть полностью одинаковой.

Пример ввода и вывода

Помимо правил, есть еще один вариант использования.

  • Входы

    Введите пример абзаца 1:

    Code Review is a question and answer site for seeking peer review of your code.
    It's built and run by you as part of the Stack Exchange network of Q&A sites.
    We're working together to improve the skills of programmers worldwide by taking working code and making it better.
    We're a little bit different from other sites. Here's how:
    Ask questions, get answers, no distractions
    This site is all about getting answers.
    It's not a discussion forum.
    There's no chit-chat.
    

    Пример ввода абзаца 2:

    We're a little bit different from other sites. Here's how:
    Ask questions, get answers, no distractions
    This site is all about getting answers.
    It's not a discussion forum.
    There's no chit-chat.
    Good answers are voted up and rise to the top.
    The best answers show up first so that they are always easy to find.
    The person who asked can mark one answer as "accepted".
    Accepting doesn't mean it's the best answer, it just means that it worked for the person who asked.
    Get answers to practical, detailed questions
    Focus on questions about an actual problem you have faced.
    Include details about what you have tried and exactly what you are trying to do.
    
  • Ожидаемый результат

    введите описание изображения здесь

    Два блока текста одинаковы, поэтому после объединения оставьте одну перекрывающуюся часть.

    введите описание изображения здесь

    Code Review is a question and answer site for seeking peer review of your code.
    It's built and run by you as part of the Stack Exchange network of Q&A sites.
    We're working together to improve the skills of programmers worldwide by taking working code and making it better.
    We're a little bit different from other sites. Here's how:
    Ask questions, get answers, no distractions
    This site is all about getting answers.
    It's not a discussion forum.
    There's no chit-chat.
    Good answers are voted up and rise to the top.
    The best answers show up first so that they are always easy to find.
    The person who asked can mark one answer as "accepted".
    Accepting doesn't mean it's the best answer, it just means that it worked for the person who asked.
    Get answers to practical, detailed questions
    Focus on questions about an actual problem you have faced.
    Include details about what you have tried and exactly what you are trying to do.
    

Экспериментальная реализация

Экспериментальная реализация приведена ниже.

[Serializable]
class ParagraphMerger
{
    private List<string> paragraphContents;

    public ParagraphMerger(List<string> input)
    {
        this.paragraphContents = input;
    }

    public ParagraphMerger(string[] input)
    {
        this.paragraphContents = input.ToList();
    }

    public ParagraphMerger Update(List<string> input)
    {
        return Update(input.ToArray());
    }

    public ParagraphMerger Update(string[] input)
    {
        bool flag = false;
        foreach (var element in input)
        {
            if (((!IsStringExist(paragraphContents, element)) || flag) && (!String.IsNullOrWhiteSpace(element)))
            {
                paragraphContents.Add(element);
                flag = true;
            }
        }
        return this;
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (var element in this.paragraphContents)
        {
            sb.AppendLine(element);
        }
        return sb.ToString();
    }

    private bool IsStringExist(List<string> strings, string target)
    {
        if (strings is null)
        {
            return false;
        }
        if (target is null)
        {
            throw new ArgumentNullException();
        }
        foreach (var element in strings)
        {
            if (element.Equals(target))
            {
                return true;
            }
        }
        return false;
    }
}

Тестовые примеры

В тестовом примере здесь используйте ParagraphMerger класс с File.ReadAllText и File.WriteAllText. Содержимое «paragraph1.txt» соответствует «Входному абзацу 1» выше, а «paragraph2.txt» — «Входному абзацу 2» выше.

var paragraph1 = File.ReadAllText("paragraph1.txt").Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var paragraph2 = File.ReadAllText("paragraph2.txt").Split(new[] { Environment.NewLine }, StringSplitOptions.None);

ParagraphMerger paragraphMerger = new ParagraphMerger(paragraph1);
paragraphMerger.Update(paragraph2);

File.WriteAllText("output.txt", paragraphMerger.ToString());
Console.WriteLine(paragraphMerger.ToString());

Все предложения приветствуются. Если есть какие-либо проблемы с:

  • Производительность обработки данных

  • Именование и удобочитаемость

  • Возможные недостатки реализованных методов

, пожалуйста, дайте мне знать.

1 ответ
1

Поле только для чтения

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

Конструктор списков

Вы сохраняете ссылку на список, переданный аргументом, вместо этого сохраняете копию, потому что при изменении переданного списка вы измените исходный список. Это может вызвать неожиданное поведение внешнего кода. input.ToList() делает копию коллекции, даже если это уже список.

IEnumerable

Вы заставили класс принимать оба массива в виде списков. Вы можете использовать IEnumerable<string> вместо этого это можно сравнить с обоими.

IsStringExist ()

Рассмотрите возможность использования Список.Содержит (T) вместо того Equals в цикле.

strings is null можно переместить в return заявление

Обновить()

Имя можно улучшить, например AppendLines() в зависимости от того, что делает метод.

В нескольких условиях на логическое выражение сначала может быть проверено самое быстрое. Шаг flag на первое место, затем IsStringExist() не будет называться, если flag является true. Но !String.IsNullOrWhiteSpace(element) можно переместить впереди всех удалить null проверка аргумента от IsStringExist() метод. Тогда IsStringExist() будет оптимизирован для одиночного List.Contains() вызов и оптимизированный как избыточный.

Маленькие бедра

Как вариант !String.IsNullOrWhiteSpace(element) можно заменить на element?.Trim().Length > 0 но это ничего не изменит.

ToString() можно сделать с помощью простых return string.Join(Environment.NewLine, paragraphContents). string.Join использует StringBuilder под капотом тоже. Разница в поведении — отсутствие CRLF в конце вывода.

[Serializable]
class ParagraphMerger
{
    private readonly List<string> paragraphContents;

    public ParagraphMerger(IEnumerable<string> input)
    {
        paragraphContents = input.ToList();
    }

    public ParagraphMerger AppendLines(IEnumerable<string> input)
    {
        bool flag = false;
        foreach (var element in input)
        {
            if (element?.Trim().Length > 0 && (flag || !paragraphContents.Contains(element)))
            {
                paragraphContents.Add(element);
                flag = true;
            }
        }
        return this;
    }

    public override string ToString()
    {
        return string.Join(Environment.NewLine, paragraphContents);
    }
}

И напоследок пример использования

var paragraph1 = File.ReadAllLines("paragraph1.txt"); // File class have a lot of interesting methods, check the docs
var paragraph2 = File.ReadAllLines("paragraph2.txt");

ParagraphMerger paragraphMerger = new ParagraphMerger(paragraph1);
paragraphMerger.AppendLines(paragraph2);

string text = paragraphMerger.ToString(); // call ToString() once and reuse
File.WriteAllText("output.txt", text);
Console.WriteLine(text);

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *