Вопрос или проблема
У меня есть HTML-файл, и я хочу заменить строку, которая содержится между двумя тегами таблицы HTML. Вот пример того, что у меня есть:
<tr class="noBorder"><td>string1</td><td>string2</td><td>string3</td><td>string4</td><td>random string</td></tr>
Я хочу заменить только random string
на что-то другое, только в одной конкретной строке. В моей таблице ячейка, содержащая random string
, является последней ячейкой.
Я пробовал это:
sed -e '28s/\(<td>\).*\(<\/td><\/tr>\)/<td>!!переменная вместо случайного!!<\/td><\/tr>/' file.html
но это заменило на следующее (поэтому я потерял все, что было до этого):
<tr class="noBorder"><td>!!переменная вместо случайного!!</td></tr>
То, что я пытаюсь достичь, это:
<tr class="noBorder"><td>string1</td><td>string2</td><td>string3</td><td>string4</td><td>!!переменная вместо случайного!!</td></tr>
Спасибо всем, кто может помочь мне решить эту проблему.
Не используйте регулярные выражения для парсинга HTML. Используйте парсер HTML.
Как указал Ferrybig в комментариях, метод с использованием xmlstarlet
работает хорошо только когда целевой HTML является подмножеством XML и не сработает, например, на саморазмыкающихся тегах, таких как <br />
и других.
Немного более сложный метод будет заключаться в использовании Perl с фреймворком Mojolicious (sudo apt install libmojolicious-perl
), который предоставляет класс Mojo::DOM, который может парсить HTML; его метод find()
будет принимать селектора Mojo::DOM::CSS (которые практически повторяют стандартные CSS-селекторы), в отличие от путей XPath xmlstarlet
; преимуществом этого метода является то, что оригинальное форматирование не будет потеряно, в отличие от метода xmlstarlet
.
Учитывая следующее:
<!DOCTYPE html>
<html>
<head>
<title>Foo</title>
</head>
<body>
<!-- Таблица "foo" -->
<table id="foo">
<tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
<tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
<tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
</table>
</body>
</html>
Чтобы заменить содержимое последней ячейки 2-й строки в таблице foo
, вы можете сделать следующее:
perl -mMojo::DOM -0777e '
my $d = Mojo::DOM -> new;
$d ->
parse(<>) ->
find("table[id=\"foo\"] tr:nth-child(2) td:last-child") ->
[0] ->
content("Замена");
print $d -> to_string
' page.html
table[id=\"foo\"] tr:nth-child(2) td:last-child
: находит последнийtd
второйtr
первой таблицы с idfoo
(которой у вас должно быть только одно, кстати)
% perl -mMojo::DOM -0777e '
my $d = Mojo::DOM -> new;
$d ->
parse(<>) ->
find("table[id=\"foo\"] tr:nth-child(2) td:last-child") ->
[0] ->
content("Замена");
print $d -> to_string
' page.html
<!DOCTYPE html>
<html>
<head>
<title>Foo</title>
</head>
<body>
<!-- Таблица "foo" -->
<table id="foo">
<tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
<tr><td>Col1</td><td>Col2</td><td>Замена</td></tr>
<tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
</table>
</body>
</html>
Используя xmlstarlet
, вы можете сделать следующее:
xmlstarlet ed -u '//table[@id="foo"]/tr[2]/td[last()]' -v 'Замена' page.html | sed 1d
//table[@id="foo"]
: адресует все таблицы с idfoo
(которой у вас должно быть только одно, кстати)/tr[2]
: адресует второй элементtr
/td[last()]
: адресует последний элементtd
Команда sed '1d'
к сожалению, необходима, потому что xmlstarlet
вставит тег версии XML в начале. Я не нашел способа предотвратить это.
Также обратите внимание, что форматирование документа может измениться в результате парсинга:
% xmlstarlet ed -u 'html/body/table[@id="foo"]/tr[2]/td[last()]' -v 'Замена' page.html | sed 1d
<!DOCTYPE html>
<html>
<head>
<title>Foo</title>
</head>
<body>
<!-- Таблица "foo" -->
<table id="foo">
<tr>
<td>Col1</td>
<td>Col2</td>
<td>Col3</td>
</tr>
<tr>
<td>Col1</td>
<td>Col2</td>
<td>Замена</td>
</tr>
<tr>
<td>Col1</td>
<td>Col2</td>
<td>Col3</td>
</tr>
</table>
</body>
</html>
Kos дал вам правильный способ®, используйте его. Это безопаснее, более надежно, легче поддерживать в долгосрочной перспективе и т.д. Я хочу объяснить, почему ваш sed не сработал. Вы заменяете все с первого <td>
, вы не пытаетесь указать, какой <td>
изменить. Выражение .*
всегда будет пытаться найти самое длинное соответствие, поэтому <td>.*</td>
будет соответствовать от первого <td>
до последнего </td>
. Вы также используете ненужное сложное выражение. Никакие \(
не нужны.
Эта команда sed
сделает то, что вам нужно, если у вас есть эта точная строка на 28-й строке вашего файла:
sed '28s/<td>[^<]*/<td>!!переменная вместо случайного!!/5' file
Оператор s
может принимать число в конце для обозначения “заменить N-е вхождение этого шаблона”. В этом случае вы хотите заменить 5-й <td>
и весь текст после него до первого <
на вашу замену, поэтому мы ищем <td>
и затем 0 или более не-<
символов ([^<]*
), и заменяем это снова на <td>
и ваш текст замены. Это выглядит так:
$ sed '28s|<td>[^<]*|<td>!!переменная вместо случайного!!|5' file | awk NR==28
<tr class="noBorder"><td>string1</td><td>string2</td><td>string3</td><td>string4</td><td>!!переменная вместо случайного!!</td></tr>
Но серьезно, не делайте этого. Это сломается при малейшем изменении формата ввода, не сможет работать, если таблица правильно отформатирована на несколько строк и т.д. Просто используйте подходящий парсер как предложил Kos.
Ответ или решение
Чтобы заменить случайную строку в HTML-таблице, разумно воспользоваться специализированным парсером HTML, так как использование регулярных выражений для разбора HTML может привести к непредсказуемым результатам. В данной ситуации, вам необходимо заменить строку "random string" на другую строку в последней ячейке определенной строки таблицы. Рассмотрим эффективный способ сделать это с помощью Perl и библиотеки Mojo::DOM.
Шаги для замены строки в HTML-таблице
-
Установка необходимых инструментов:
Убедитесь, что в вашей системе установлен Perl и библиотека Mojo::DOM. Для установки Mojolicious выполните команду:sudo apt install libmojolicious-perl
-
Создание скрипта для замены:
Напишите скрипт на Perl, который будет загружать ваш HTML-файл, находить нужный элемент и заменять его содержимое. Вот пример кода, который выполняет данную задачу. Предположим, вы хотите заменить содержимое последней ячейки второй строки таблицы с ID "foo":perl -mMojo::DOM -0777e ' my $d = Mojo::DOM->new; $d->parse(<>) # Загружаем содержимое HTML ->find("table[id=\"foo\"] tr:nth-child(2) td:last-child") # Ищем нужную ячейку ->content("!!проверяемый текст вместо случайного!!"); # Заменяем содержимое print $d->to_string; # Выводим измененное HTML ' ваш_файл.html
-
Объяснение кода:
-mMojo::DOM
: Загружает модуль Mojo::DOM.-0777e
: Загружает весь файл в одну строку, благодаря чему можно использовать большие объёмы текста.$d->parse(<>);
: Загружает содержание HTML-файла.find("table[id=\"foo\"] tr:nth-child(2) td:last-child")
: Находит последний элемент<td>
второй строки соответствующей таблицы.content("!!проверяемый текст вместо случайного!!");
: Производит замену содержимого найденного элемента.print $d->to_string;
: Печатает измененное HTML на стандартный вывод.
-
Запуск скрипта:
Сохраните приведённый выше код в файл или выполните его через терминал, перенаправив входные данные из вашего HTML файла.
Заключение
Использование библиотек, таких как Mojo::DOM, обеспечивает надежность и безопасность вашего кода, упрощая манипуляции с HTML-документами. Это особенно пригодится при обновлении веб-контента, где критически важно сохранять первоначальную структуру и форматирование. Отказ от regex для работы с HTML значительно снижает вероятность ошибок, улучшая долгосрочную поддерживаемость вашего решения.
Эти рекомендации помогут вам успешно заменить строку в HTML-таблице эффективно и безопасно. Оставайтесь на шаг впереди в ваших профессиональных разработках, используя доступные инструменты для решения задач.