Как я могу пометить текст на основе отступа?
Я хочу пометить текстовое содержимое (с помощью XML-подобных тегов) на основе глубины отступа. Пустые строки должны быть сохранены. В приведенном ниже примере содержание имеет отступ с 1, 2 или 3 вкладками.
ВХОД
aaa bbb bbb aaa ccc ccc bbb bbb
Я хочу сгруппировать строки на одном уровне отступов и перевести эти уровни отступов в теги x
, y
а также z
, как это:
ВЫХОД
<x>aaa</x>
<y>bbb
bbb</y>
<x>aaa</x>
<z>ccc
ccc</z>
<y>bbb
bbb</y>
Как я могу это сделать?
1 ответ
Постановка задачи:
Входной файл содержит текст с отступом в ноль или более символов табуляции. В частности, каждая строка на входе является одной из них:
- Пустой или
- Ноль или более вкладок (до предела; см. Ниже), за которыми следует символ, который не является ни пробелом, ни табуляцией (за которым следует ноль или более любого символа).
Там нет строк, которые
- Начните с нуля или более вкладок, а затем пробел.
(Это означает, что нет строк, начинающихся с пробела.)
или же - Состоят полностью из одной или нескольких вкладок (и ничего больше).
или же - Начните с более чем определенного количества вкладок.
Входные данные должны быть логически разбиты на группы строк, которые все либо
- Пустой или
- Отступы с одинаковым количеством вкладок.
Пустые строки должны быть переданы на выход без изменений.
Должен быть указан список тегов; например, x
, y
, а также zz
, Группа (непустых) строк, которые имеют нулевые табуляции (т.е. не имеют отступов), заключаются в квадратные скобки <x>
а также </x>
, Группа строк с отступом в одну вкладку должна заключаться в квадратные скобки <y>
а также </y>
, Группа строк с двумя вкладками должна заключаться в квадратные скобки <zz>
а также </zz>
, (Строки не будут иметь отступ с более чем двумя вкладками.)
Первая строка группы (непустой строки) должна иметь начальный тег, вставленный между вкладками и текстом. К последней строке группы должен быть добавлен конечный тег в конце текста. Группа может состоять из одной строки, поэтому первая строка также может быть последней строкой. Все строки группы, кроме первой, должны иметь дополнительный отступ (с пробелами, вставленными между вкладками и текстом) по ширине начального тега.
Например (используя ―→
представлять вкладку), этот ВХОД:
aaa
―→ Once upon a midnight dreary,
―→ while I pondered, weak and weary,
Quoth the Raven, “Nevermore.”
―→ ―→ The quick brown fox
―→ ―→ jumps over the lazy dog.
―→ It was a dark and stormy night.
―→ Suddenly a shot rang out.
должны быть переведены в этот выход:
<x>aaa</x>
―→ <y>Once upon a midnight dreary,
―→ while I pondered, weak and weary,</y>
<x>Quoth the Raven, “Nevermore.”</x>
―→ ―→ <zz>The quick brown fox
―→ ―→ jumps over the lazy dog.</zz>
―→ <y>It was a dark and stormy night.
―→ Suddenly a shot rang out.</y>
Решение:
Очевидно, мы не совсем знаем, что делать со строкой ввода, пока не прочитаем следующую строку. Эта проблема обычно решается путем сохранения содержимого одной строки для обработки после прочтения следующей.
Итак, вот оно:
awk '
BEGIN {
num_tags = split("x y zz", tags)
for (i=1; i<=num_tags; i++)
{
len = length(tags[i]) + 2
tag_pad[i] = ""
for (j=1; j<=len; j++) tag_pad[i] = tag_pad[i] " "
}
}
{
if (NF == 0)
indent_num = 0
else
{
indent_num = index($0, $1)
indent_str = substr($0, 1, indent_num-1)
restOfLine = substr($0, indent_num)
}
if (indent_num != saved_indent_num && saved != "")
{
print saved "</" tags[saved_indent_num] ">"
saved = ""
}
if (NF == 0)
print
else if (indent_num > num_tags)
{
errmsg = "Error: line %d has an indent level of %d.\n"
printf errmsg, NR, indent_num > "/dev/stderr"
exit 1
}
else if (indent_num == saved_indent_num)
{
print saved
saved = indent_str tag_pad[indent_num] restOfLine
}
else
saved = indent_str "<" tags[indent_num] ">" restOfLine
saved_indent_num = indent_num
}
END {
if (saved != "")
print saved "</" tags[saved_indent_num] ">"
}
'
Блок BEGIN инициализирует теги (x
, y
, а также zz
), разделив пробелом строку. tag_pad
массив содержит достаточно места, чтобы соответствовать ширине тегов (включая <
а также >
): tag_pad[1]
а также tag_pad[2]
три пробела; tag_pad[3]
это четыре пробела.
Прочитав строку ввода, мы ее анализируем. Если нет полей (NF == 0
), он должен быть пустым (поскольку мы указали, что ни одна строка не состоит полностью из пробелов и табуляций), поэтому установите indent_num
до 0. В противном случае измерьте отступ, найдя местоположение $1
(первое слово) в $0
(вся строка). index
возвращает значение, начинающееся с 1, так что на самом деле это на один больше, чем количество пробельных символов перед первым непробельным символом (и, помните, мы предполагаем, что это все вкладки). Это повезло, потому что сейчас indent_num
соответствует записям в tags
а также tag_pad
массивы. Затем мы разделяем линию на indent_str
(пробел) и restOfLine
(все после отступа).
Теперь мы полагаемся на сохраненную информацию. Если эта строка имеет отступ, отличный от предыдущего, мы начинаем новую группу. Если есть сохраненная строка, запишите ее с соответствующим конечным тегом в конце строки.
Если текущая строка пуста, просто распечатайте ее. Проверьте, не является ли текущий уровень отступа слишком высоким, и выведите его, если это так. Если текущий отступ такой же, как и предыдущий, это строка продолжения уже запущенной группы, поэтому просто напечатайте сохраненную (предыдущую) строку и создайте новую saved
строка, которая является текущей строкой с шириной текущего тега, вставленного между отступом и текстом. В противном случае мы начинаем новую группу, поэтому создайте saved
строка, которая является текущей строкой с начальным тегом (самим), вставленным между отступом и текстом.
Когда мы доберемся до конца ввода, закончим текущую группу, как мы делали раньше.