Perl: ввод текста в новую строку при обнаружении определенного символа - PullRequest
0 голосов
/ 11 июня 2011

У меня большой непрерывный текст с такими символами, как {, },//,; и пробелами между ними.Я хочу прочитать этот текст и написать в новой строке, где бы он ни находил эти символы.

Входной текст имеет вид:

apple{{mango } guava ; banana; // pear      berry;}

Ожидаемые отформатированные выходные данные должны быть такими, как показано на рисунке

apple
{
{
mango
}
guava ;
banana;
// pear
berry;
}

Я хочу сделать это в perl . Заранее спасибо.

Ответы [ 4 ]

4 голосов
/ 12 июня 2011

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

#!/usr/bin/perl
use strict;
use warnings;

my $string = 'apple{{mango } guava ; banana; // pear      berry;}';
my $new_string = join("\n", grep {/\S/} split(/(\W)/, $string));

print $new_string . "\n";

Это разбивает строку на массив, разбивая на несловарные символы, но сохраняя элемент. Затем greps для непробельных символов (удаляя элементы массива, содержащие пробелы). Затем объединяет элементы массива с символами новой строки в одну строку. Из того, что в вашей спецификации говорится, что вам нужно // вместе, я оставлю это в качестве упражнения для читателя.

Edit: Посмотрев на ваш запрос еще раз, вы получите определенную, но сложную структуру, которую вы пытаетесь проанализировать. Чтобы сделать это правильно, вам может понадобиться что-то более мощное, например, модуль Regexp::Grammars. Это займет некоторое обучение, но вы можете определить очень сложный набор инструкций по синтаксическому анализу, чтобы сделать именно то, что вам нужно.

Редактировать 2: Поскольку я искал причину узнать больше о Regexp::Grammars, я воспользовался этой возможностью. Это основной пример, который я придумал. Он распечатывает проанализированную структуру данных в файл с именем "log.txt". Я знаю, что это не похоже на структуру, о которой вы просили, но она содержит всю эту информацию и может быть воссоздана так, как вам нравится. Я сделал это с помощью рекурсивной функции, которая в основном противоположна синтаксическому анализатору.

#!/usr/bin/env perl
use strict;
use warnings;

use Data::Dumper;
use Regexp::Grammars;

my $grammar = qr{
  <nocontext:>
  <Line>
  <rule: Line>      <[Element]>*
  <rule: Element>   <Words> | <Block> | <Command> | <Comment>
  <rule: Command>   <[Words]> ;
  <rule: Block>     \{ <[Element]>* \}
  <rule: Comment>   // .*? \s{2,}        #/ Syntax Highlighter fix
  <rule: Words>     (?:\b\w+\b) ** \s
}x;

my $string = 'apple{{mango kiwi } guava ; banana; // pear      berry;}';

if ($string =~ $grammar) {
  open my $log, ">", "log.txt";
  print $log Dumper \%/; #/

  print elements($/{Line}{Element});

} else {
  die "Did not match";
}

sub elements {
  my @elements = @{ shift() };
  my $indent = shift || 0;
  my $output;

  foreach my $element (@elements) {
    $output .= "\t" x $indent;

    foreach my $key (keys %$element) {
      if ($key eq 'Words') {
        $output .= $element->{$key} . "\n";
      } elsif ($key eq 'Block') {
        $output .= "{\n" . elements($element->{$key}->{Element}, $indent + 1) . ("\t" x $indent) . "}\n";
      } elsif ($key eq 'Comment') {
        $output .= $element->{$key} . "\n";
      } elsif ($key eq 'Command') {
        $output .= join(" ", @{ $element->{$key}->{Words} }) . ";\n";
      } elsif ($key eq 'Element') {
        $output .= elements($element->{$key}, $indent + 1);
      }
    }
  }

  return $output;
}

Редактировать 3: В свете комментариев от ОП, я адаптировал приведенный выше пример, чтобы разрешить использование нескольких слов в одной строке, поскольку на данный момент эти слова могут быть разделены только одним пробелом. Я также сделал комментарии, совпадающие со всем, что начинается в // и заканчивается двумя или более пробелами. Кроме того, так как я вносил изменения, и так как я считаю, что это довольно красивый код, я добавил табуляцию в блок форматирования блоков. Если это не желательно, это должно быть достаточно легко убрать. Иди сейчас и изучи Regexp::Grammars и сделай так, чтобы она подходила для твоего конкретного случая. (Я знаю, я должен был заставить ОП сделать это изменение, но мне тоже нравится его изучать)

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

#!/usr/bin/perl
use strict;
use warnings;

my $string = 'apple{{mango } guava ; banana; // pear      berry;}';
my $new_string = join("\n", split(/((?:\/\/).*?\s{2,})/, $string));

print $new_string . "\n";

чей вывод

apple{{mango } guava ; banana; 
// pear      
berry;}
3 голосов
/ 11 июня 2011

Ваша спецификация отстой.Иногда вы хотите новую строку до и после.Иногда вы хотите новую строку после.Иногда вы хотите новую строку раньше.У вас есть «груша» и «ягода» в отдельных строках, но это не соответствует ни одному из условий в вашей спецификации.

Качество ответа прямо пропорционально внимательности, данной при составлении вопроса.1003 *

С неосторожным вопросом вы, вероятно, получите небрежный ответ.

#!/usr/bin/perl
use warnings;
use strict;

$_ = 'apple{{mango } guava ; banana; // pear      berry;}';

s#([{}])#\n$1\n#g; # curlies
s#;#;\n#g;         # semicolons
s#//#\n//#g;       # double slashes
s#\s\s+#\n#g;      # 2 or more whitespace
s#\n\n#\n#g;       # no blank lines

print;
1 голос
/ 18 июня 2011

Как вы сказали, это не домашняя работа, на ум приходит что-то вроде следующего:

my $keeps  = qr#(//\s+\w+)#;            #special tokens to keep  (e.g., // perl)
my $breaks = qr#(\s+|\[|\]|\{|\})#;     #simple tokens to split words at

while ( my $text = <> )
{
    @tokens = grep /\S/, split( qr($keeps|$breaks), $text );
    print join(".\n.", @tokens ), "\n";
}

Вам придется самим выработать настоящие правила.

1 голос
/ 11 июня 2011

Не совсем то, что вы хотите, но для начала имхо будет достаточно:

echo 'apple{{mango } guava ; banana; // pear      berry;}' |\
perl -ple 's/(\b\w+\b)/\n$1\n/g'

даст:

apple
{{
mango
 } 
guava
 ; 
banana
; // 
pear

berry
;}

Вы можете начать улучшать его ...

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