Генератор текстовых блоков на C#


При работе с консольными приложениями или при логировании в файл часто бывает полезно выделить элементы текста. Один из способов добиться этого - нарисовать прямоугольник вокруг текста, используя стандартные символы из доступного набора символов.



Класс BoxGenerator

При работе с обычным текстом, например в консольном приложении, может потребоваться привлечь внимание к определенным элементам. Один из способов сделать это - нарисовать прямоугольник вокруг текста. Конечно, при использовании обычного текста возможности рисования ограничены. Если вы используете шрифт с пропорционально расположенными символами, создать идеальную рамку может оказаться невозможным. С помощью моноширинных шрифтов, таких как те, которые обычно используются в консольных приложениях, вы можете использовать один символ для представления границы или использовать некоторые символы выбранного шрифта с различными вариантами горизонтальных, вертикальных и угловых элементов поля.

В этой статье мы создадим простой класс BoxGenerator, который будет обрабатывать массив строк, каждая из которых представляет собой одну строку текста. Эти строки будут объединены в одну строку, которую можно вывести или записать в лог. Он будет содержать строки из массива, окруженные прямоугольником.

Существует множество способов, с помощью которых можно нарисовать прямоугольник с помощью символов. Мы допустим три варианта, хотя вы можете добавить новые стили коробки относительно легко. Три стиля, которые мы будем поддерживать, - это одинарная или двойная граница, использующая восемь общих символов, или коробка, сделанная из повторяющегося одного символа. Эти варианты проиллюстрированы ниже:

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

Создание типов

Для начала нам нужен класс. Мы назовем его "BoxGenerator". Создайте новое консольное приложение и добавьте класс. Измените код, чтобы сделать класс общедоступным, как показано ниже:

public class BoxGenerator
{
}
Чтобы сделать код читабельным, было бы полезно включить перечисление, включающее типы границ. Добавьте новый тип с именем "BoxStyle" и определите перечисление следующим образом:

public enum BoxStyle
{
    Single,
    Double,
    SingleCharacter
}
Определения стиля границы

Существует множество способов определения символов, используемых для каждого стиля границ. Например, можно настроить массивы символов или строки. Для простоты мы определим три строки, каждая из которых содержит восемь символов. Положение символов в строке будет определять положение, в котором символы будут отображаться в поле. Давайте сопоставим порядок, в котором будут использоваться символы, с их использованием. Это даст следующие индексы:

0. Слева вверху.
1. Верх.
2. Справа вверху.
3. Слева.
4. Справа.
5. Внизу слева.
6. Низ
7. Внизу справа.


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

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

string _singleLine = "┌─┐││└─┘";
string _doubleLine = "╔═╗║║╚═╝";
string _defaultBorder = "████████";
При использовании стилей границ нам нужно будет где-то хранить используемый стиль. Добавьте еще одно поле для этой цели:

string _border;
Магические числа

Когда мы извлекаем символы из строк стиля, нам нужно будет ссылаться на правильный индекс для каждой позиции в поле. Мы могли бы использовать целочисленные литералы или магические числав коде, но это снизило бы читабельность. Лучше всего определить набор констант. Добавьте следующие константы в класс BoxGenerator:

const int TL = 0;
const int T = 1;
const int TR = 2;
const int L = 3;
const int R = 4;
const int BL = 5;
const int B = 6;
const int BR = 7;
Конструкторы

Мы настроим каждый экземпляр BoxGenerator в конструкторе, либо выбрав соответствующую строку конфигурации, либо создав новую из одного символа. При использовании конструктора по умолчанию BoxGenerator будет использовать строку границы по умолчанию, копируя ее в поле _border следующим образом:

public BoxGenerator()
{
    _border = _defaultBorder;
}
Давайте добавим второй конструктор, который позволяет выбрать один из стилей из перечисления BoxStyle. Этот конструктор выбирает одну из заданных строк границы и копирует ее в поле _border. Если неизвестный стиль или стиль одного символа передается параметру конструктора , мы будем использовать границу по умолчанию.

public BoxGenerator(BoxStyle style)
{
    switch (style)
    {
        case BoxStyle.Single: _border = _singleLine; break;
        case BoxStyle.Double: _border = _doubleLine; break;
        default: _border = _defaultBorder; break;
    }
}
Наконец, мы можем добавить конструктор, который настраивает BoxGenerator на использование одного символа для каждой части границы поля:

public BoxGenerator(char character)
{
    _border = new string(character, 8);
}
Добавление метода GenerateBox

Последнее задание - добавить метод, который преобразует строковый массив в строку, содержащую текст и поле. В зависимости от использования генератора вы можете ограничить ширину коробки. Для этого мы будем использовать два параметра. Первый будет строковым массивом, а второй - максимальной шириной текста внутри поля. Сама коробка может быть на два символа шире, чем это.

Есть несколько способов, с помощью которых мы можем использовать максимальную ширину. Мы могли бы обрезать любые строки текста, которые длиннее значения. Мы также могли бы разделить строки, возможно, используя алгоритм переноса слов, и завернуть весь текст в коробку. Для класса BoxGenerator мы будем использовать первый подход. Было бы относительно легко разделить строки перед добавлением коробки, если это требуется.

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

Каждый этап, кроме проверки, может быть выполнен в частном методе. Мы будем вызывать каждый из них из одного общедоступного метода класса " GenerateBox":

public string GenerateBox(string[] text, int maxInternalWidth)
{
    if (text == null) throw new ArgumentNullException("text");
    if (maxInternalWidth < 1) throw new ArgumentException("width too small");
 
    int width = GetWidth(text, maxInternalWidth);
    var padded = PadText(text, width);
    return BuildBox(padded, width);
}
Определение ширины текста

Если какая-либо строка предоставленного текста больше по длине, чем максимальная длина, предоставленная методу GenerateBox, мы создадим поле с внутренним размером, соответствующим максимальной длине, и усечем все строки, которые слишком длинны, чтобы поместиться. Если все строки текста короче максимальной внутренней ширины, мы сделаем так, чтобы внутренняя часть поля была такой же ширины, как и самая длинная строка.

Чтобы определить ширину, нам нужно найти самую длинную строку текста и сравнить ее с максимальным размером. Меньшее из этих двух значений-это требуемая ширина. Мы рассчитаем длину в приватном методе GetWidth:

private int GetWidth(string[] text, int maxWidth)
{
    int largest = text.Max(s => s.Length);
    return Math.Min(largest, maxWidth);
}
Примечание: если вы используете версию .NET framework до версии 3.5 можно было заменить запрос LINQ циклом foreach и условным оператором.

Заполнение текста

Второй метод дополняет или усекает текст до нужной длины. Самый простой способ сделать это - использовать методы PadRight или Substring для добавления пробелов в конец строки или извлечения начала строки соответственно:

private IEnumerable<string> PadText(string[] text, int width)
{
    return text.Select(s =>
        s.Length > width ? s.Substring(0, width) : s.PadRight(width, ' '));
}
Построение коробки

Последняя задача - объединить границы и текст в единую строку. Чтобы избежать нескольких строковых операций, мы будем использовать StringBuilder. Сначала мы создадим верхнюю границу, затем добавим текстовые строки и, наконец, нижнюю границу. Строки добавляются в отдельном методе, который циклически проходит через массив, добавляя строку с левыми и правыми символами границы для каждого элемента:

private string BuildBox(IEnumerable<string> padded, int width)
{
    StringBuilder sb = new StringBuilder();
    sb.Append(_border[TL]).Append(_border[T], width).Append(_border[TR]).AppendLine();
    AddLines(padded, width, sb);
    sb.Append(_border[BL]).Append(_border[B], width).Append(_border[BR]);
    return sb.ToString();
}
 
private void AddLines(IEnumerable<string> padded, int width, StringBuilder sb)
{
    foreach (string line in padded)
    {
        sb.Append(_border[L]).Append(line).Append(_border[R]).AppendLine();
    }
}
Использование генератора коробок

Теперь вы можете попробовать класс BoxGenerator. В следующем примере создается новый экземпляр, указывающий на использование стиля двойной границы. Затем он выводит некоторый текст в поле.

var generator = new BoxGenerator(BoxStyle.Double);
 
string[] text = new string[]
{
    "TEST",
    "----",
    "",
    "The quick brown fox jumps over the lazy dog."
};
 
Console.WriteLine(generator.GenerateBox(text, 75));
 
/* OUTPUT
 
╔════════════════════════════════════════════╗
║TEST                                        ║
║----                                        ║
║                                            ║
║The quick brown fox jumps over the lazy dog.║
╚════════════════════════════════════════════╝
 
*/
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, си шарп, консоль, ввод-вывод




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.



Две семьи
Программы для работы с сайтами
Урок 25. Несколько операторов Else If в JavaScript