Замените кавычки внутри инкапсулированной строки, используя регулярные выражения Perl - PullRequest
0 голосов
/ 07 февраля 2019

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

Я пытался использовать приведенную ниже строку Perl, чтобы заменить кавычки обратным тиком `, но яне уверен, как заменить только кавычки, а не всю группу 1.

Пример данных (test.txt):

"1"|"Text"|"a"\n
"2"|""Text in quotes""|"ab"\n
"3"|"Text "around" quotes"|"abc"\n

perl -pi.bak -e 's/(?<=\|")(.*)(?="\|)/\1`/' test.txt

Вот что происходит:

"1"|"`"|"a"\n
"2"|"`"|"ab"\n
"3"|"`"|"abc"\n

Вот что я пытаюсь достичь:

"1"|"Text"|"a"\n
"2"|"`Text in quotes`"|"ab"\n
"3"|"Text `around` quotes"|"abc"\n

Ответы [ 4 ]

0 голосов
/ 08 февраля 2019

Другой Perl.После разбиения по массиву @F, проверьте «не в начале / конце элементов.

 perl -F"\|"  -lane   ' for(@F) { s/(?<!^)"(?!$)/`/g }; print join("|",@F) ' 

с заданными входами

$ cat grasshopper.txt
"1"|"Text"|"a"
"2"|""Text in quotes""|"ab"
"3"|"Text "around" quotes"|"abc"
$  perl -F"\|"  -lane   ' for(@F) { s/(?<!^)"(?!$)/`/g }; print join("|",@F) ' grasshopper.txt
"1"|"Text"|"a"
"2"|"`Text in quotes`"|"ab"
"3"|"Text `around` quotes"|"abc"
$
0 голосов
/ 07 февраля 2019

Обновлен для уточнения того, что обратные галочки, которые уже присутствуют, должны быть удвоены


Один из способов - split на | и обрезать заключающие в кавычки, чтобы сделать оставшиесярегулярное выражение, затем соберите строку обратно.Это может потерять некоторую эффективность по сравнению с одним регулярным выражением, но гораздо проще поддерживать

perl -F"\|" -wlanE'
    say join "\|", 
        map { s/^"|"$//g; s/`/``/g; s/"([^"]+)"/`$1`/g; qq("$_") } @F
' data.txt

Опция -a делает его "автоматически разбитым" на каждую строку, поэтому в программе токены строк доступны в @F, а -F указывает шаблон для разделения (кроме значения по умолчанию).-l обрабатывает переводы строк.См. Командные переключатели в perlrun .

В map включающие " s удалены, а любые существующие обратные помехи удвоены;затем " вокруг шаблонов меняются глобально.Затем кавычки возвращаются и возвращаемый список join -ed.| в join экранируется, чтобы пропустить его через оболочку в программу Perl;если это входит в сценарий (вместо однострочного), что я всегда рекомендую, замените это \| на |.

Я не знаю типичные данные и возможные крайние случаиотносительно цитат, но если могут быть свободные (одиночные, непарные) цитаты, вышеупомянутые будут иметь проблемы и могут привести к неправильному выводу, и тихо;как любая процедура, которая ожидает парные кавычки, без чрезвычайно подробного анализа.

В целом безопаснее просто заменить все " (кроме включенных) на

map { s/^"|"$//g; s/`/``/g; s/"/`/g; qq("$_") }

(или tr вместо регулярного выражения s///g).Это также добавляет некоторую меру эффективности.


Еще один способ добраться до «мяса» данных - использовать Text :: CSV , который позволяет использовать разделитель, отличный от (по умолчанию) и поглощает кавычки.Наличие кавычек внутри полей считается плохим CSV, но модуль может также хорошо это проанализировать, с вариантами выбора ниже.

use warnings;
use strict;
use feature 'say';

use Text::CSV;

my $file = shift || 'data.txt';
my $outfile = 'new_' . $file;

my $csv = Text::CSV->new( { binary => 1, sep_char => '|', 
    allow_loose_quotes => 1, escape_char => '',     # quotes inside fields
    always_quote => 1                               # output as desired
} ) or die "Can't do CSV: ", Text::CSV->error_diag;

open my $fh,     '<', $file    or die "Can't open $file: $!";
open my $out_fh, '>', $outfile or die "Can't open $outfile: $!";

while (my $row = $csv->getline($fh)) {
    s/`/``/g for @$row;
    tr/"/`/  for @$row;
    $csv->say($out_fh, $row);
}

Для работы с кавычками внутри полей escape_char должен отличаться от quote_char;Я просто установил здесь значение ''.Выходные данные также обрабатываются модулем, и атрибут always_quote предназначен для этого (чтобы процитировать все поля, необходимые или нет).Пожалуйста, смотрите документацию.

Конечно, с этим модулем можно сделать гораздо больше.

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


Несколько вопросов

  • Чтокакие данные есть и есть ли возможность иметь случайную цитату?Тогда что?Это может повлиять даже на выбор оптимального подхода, поскольку может потребовать детального анализа.

  • Если задача состоит в том, чтобы выпрямить данные в стиле CSV, то почему бы не удвоить кавычки внутри полей?как обычное и правильное в CSV, вместо того, чтобы заменить их (и потенциально повредить их текстовое значение)?См., Например, документы модуля.

0 голосов
/ 07 февраля 2019

Perl использует $ 1 в качестве заполнителя для первой группы захвата в замещающей части регулярного выражения вместо \ 1 (используется в соответствующей части регулярного выражения).Ваше регулярное выражение не соответствует внутренним кавычкам и не сможет соответствовать первому или последнему полю ваших данных, разделенных каналом.Ваша подстановка также не содержит символа кавычки перед захваченной группой.

Попробуйте:

perl -pi.bak -e 's/(?<=(?:^|\|)")"([^"]*)"(?="(?:$|\|))/`$1´/' test.txt
0 голосов
/ 07 февраля 2019

В Perl 5.14 и новее вы можете использовать

perl -pi.bak -e 's/(?:^|\|)(")?\K(.*?)(?=\1(?:$|\|))/$2=~s#"|(`)#`$1#gr/ge' test.txt

См. regex demo и онлайн-демо .

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

Подробности

  • (?:^|\|) - соответствует началу строки или |
  • (")? - необязательная группа 1, соответствующая "
  • \K - оператор сброса соответствияотбрасывание всего текста в текущем буфере совпадений
  • (.*?) - Группа 2: любые 0+ символов, кроме символов перевода строки
  • (?=\1(?:$|\|)) - положительный прогноз, который гарантирует, что то же самоезначение как в группе 1, а затем конец строки или | непосредственно справа от текущего местоположения.

Итак, группа 2 - это содержимое ячейки, без заключенных в двойные кавычки.$2=~s#"|() # $1#gr заменяет все " на ` и дублирует все найденные буквенные обратные пометки в значении группы 2 (см. это демонстрационное выражение регулярного выражения ).Шаблон "|(`) соответствует " или обратному трюку (захват последнего в Группу 1), а `$1 заменяет совпадение обратным тылом и содержимым Группы 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...