Perl:如何拆分保留最后一句话的段落到另一个数组?

时间:2018-05-01 09:35:41

标签: regex perl xml-parsing xml-twig

我正在尝试按<Description> 数字分割Bit文字,并将Bit 数字元素放入其中。这是文件,我正在解析。

        <Register>
                <Name>abc</Name>
                <Abstract></Abstract>
                <Description>Bit 6  random description
                    Bit 5 msg octet 2
                    Bit 4-1 
                    Bit 0 msg octet 4
                    These registers containpart of the Upstream Message. 
                    They should be written only after the cleared by hardware.
                    </Description>
        <Field>
        <Name>qwe</Name>

        <Description></Description>
        <BitFieldOffset>6</BitFieldOffset>
        <Size>1</Size>
        <AccessMode>Read/Write</AccessMode>

        </Field>
    <Field>
        <Name>qwe</Name>

        <Description></Description>
        <BitFieldOffset>5</BitFieldOffset>
        <Size>1</Size>
        <AccessMode>Read/Write</AccessMode>

        </Field>
<Field>
....
</Field>
                </Register>
            <Register>
                <Name>xyz</Name>
                <Abstract></Abstract>
                <Description>Bit 3  msg octet 1
                    Bit 2 msg octet 2
                    Bit 1 msg octet 3
                    Bit 0 msg octet 4
                    These registers. 
                    They should be written only after the cleared by hardware.
                </Description>
<Field>
....
</Field>
<Field>
....
</Field>
            </Register>

预期输出为:

Expected output:

<Register>
<long_description>
These registers containpart of the Upstream Message. 
    They should be written only after the cleared by hardware.
</long_description>

<bit_field position="6" width=" 1">
<long_description>
<p> random description</p>
</long_description>
<bit_field position="5" width=" 1">
<long_description>
<p>...</p>
</long_description>
<bit_field position="1" width=" 4">
<long_description>
<p>...</p>
</long_description>

</Register>

<Register>
.
.
.
</Register>

我正在使用XML-Twig包来解析这个文件,但却陷入了分裂。

foreach my $register ( $twig->get_xpath('//Register') ) # get each <Register>
    {

        my $reg_description= $register->first_child('Description')->text;
        .
        .
        .
          foreach my $xml_field ($register->get_xpath('Field'))
          {
             .
             .
             my @matched = split ('Bit\s+[0-9]', $reg_description);
             .
             .
           }
   }

我不知道如何相应地创建<bit_field>并将Bit以外的文字保存到<Register> <long_description>。有人可以帮忙吗?

编辑Bit中的<Description>可以有多行。例如,在以下示例中,Bit 10-9的描述直到Bit 8

开始
<Description>Bit 11 GOOF 
Bit 10-9 Clk Selection:
 00 :  8 MHz
 01 :  4 MHz
 10 :  2 MHz
 11 :  1 MHz
Bit 8 Clk Enable : 1 = Enable CLK
<Description>

1 个答案:

答案 0 :(得分:1)

如果我把一切都搞定了,你可以逐行查看整个文本块。

使用正则表达式检查一行是否与某个模式匹配。捕获相关部分。在包含存储每个位的详细信息的哈希的数组中逐位缓存。

不包含位模式的缓冲行。如果跟随另一行,包含位模式,则缓冲区必须属于最近的位。将它附加到那里。所有其他行必须是整体描述的一部分。 注意:这并不区分最后一位描述的任何其他行。如果有这么一点,它的附加行将成为整体描述的开始。 (但是你说你的数据中没有这些东西。)

概念证明:

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

my $description_in = 'Bit 6  random description
                    Bla bla additional line bla bla
                    bla bla
                    Bit 5 msg octet 2
                    Empty line below

                    Bla bla set to gain instant world domination bla bla
                    Bit 4-1
                    Bit 0 msg octet 4
                    These registers containpart of the Upstream Message.
                    They should be written only after the cleared by hardware.

                    Empty line above
                    Bla bla bla...';

my @bits = ();
my $description_overall = '';

my $line_buffer = '';
foreach my $line (split("\n", $description_in)) {
  # if line
  #  begins with optional white spaces
  #  followed by "Bit"
  #  followed by at least one white space
  #  followed by at least one digit (we capture the digits)
  #  followed by an optional sequence of optional white spaces, "-", optional white spaces and at least one digit (we capture the digits)
  #  followed by an optional sequence of at least one white space and any characters (we capture the characters)
  #  followed by the end of the line
  if ($line =~ m/^\s*Bit\s+(\d+)(?:\s*-\s*(\d+))?(?:\s+(.*?))?$/) {
    my ($position_begin, $position_end, $description) = ($1, $2, $3);
    my $width;

    # if there already are bits we've processed
    if (scalar(@bits)) {
      # the lines possibly buffered belong to the bit before the current one, so append them to its description
      $bits[$#bits]->{description} .= (length($bits[$#bits]->{description}) ? "\n" : '') . $line_buffer;
      # and reset the line buffer to collect the additional lines of the current bit;
      $line_buffer = '';
    }

    # $position_end is defined only if it was a "Bit n-m"
    # otherwise set it to $position_begin
    $position_end = defined($position_end) ? $position_end : $position_begin;

    $width = abs($position_end - $position_begin) + 1;

    # set description to the empty string if not defined (i.e. no description was found)
    $description = defined($description) ? $description : '';

    # push a ref to a new hash with the keys position, description and width into the list of bits
    push(@bits, { position => (sort({$a <=> $b} ($position_begin, $position_end)))[0], # always take the lower position
                  description => $description,
                  width => $width });
  }
  else {
    # it's not a bit pattern, so just buffer the line
    $line_buffer .= (length($line_buffer) ? "\n" : '') . $line;
  }
}
# anything still in the buffer must belong to the overall description
$description_overall .= $line_buffer;

print("<Register>\n  <long_description>\n$description_overall\n  </long_description>\n");
foreach my $bit (@bits) {
  print("  <bit_field position=\"$bit->{position}\" width=\"$bit->{width}\">\n    <long_description>\n$bit->{description}\n    </long_description>\n  </bit_field>\n")
}
print("</Register>\n");

打印:

<Register>
  <long_description>
                        These registers containpart of the Upstream Message.
                        They should be written only after the cleared by hardware.

                        Empty line above
                        Bla bla bla...
  </long_description>
  <bit_field position="6" width="1">
    <long_description>
random description
                        Bla bla additional line bla bla
                        bla bla
    </long_description>
  </bit_field>
  <bit_field position="5" width="1">
    <long_description>
msg octet 2
                        Empty line below

                        Bla bla set to gain instant world domination bla bla
    </long_description>
  </bit_field>
  <bit_field position="1" width="4">
    <long_description>

    </long_description>
  </bit_field>
  <bit_field position="0" width="1">
    <long_description>
msg octet 4
    </long_description>
  </bit_field>
</Register>             

我把它写成独立脚本,以便我可以测试它。您必须将其改编为您的脚本。

可能会添加一些整体描述的处理,消除那些长白色空间序列。

首先,我尝试使用连续模式(while ($x =~ m/^...$/gc))但是以某种方式吃掉了行结尾,导致只匹配每一行。为了使它们远离实际匹配,看起来没有用(说它没有实现;我想,我必须在这台计算机上检查我的Perl?),所以显式拆分线条是一种解决方法。

也可以使用grep() s,map()等缩短它。但我认为,冗长的版本更好地展示了这些想法。所以我甚至没有调查过。