内容

名称

Text::Balanced - 从字符串中提取分隔的文本序列。

概要

use Text::Balanced qw (
    extract_delimited
    extract_bracketed
    extract_quotelike
    extract_codeblock
    extract_variable
    extract_tagged
    extract_multiple
    gen_delimited_pat
    gen_extract_tagged
);

# Extract the initial substring of $text that is delimited by
# two (unescaped) instances of the first character in $delim.

($extracted, $remainder) = extract_delimited($text,$delim);

# Extract the initial substring of $text that is bracketed
# with a delimiter(s) specified by $delim (where the string
# in $delim contains one or more of '(){}[]<>').

($extracted, $remainder) = extract_bracketed($text,$delim);

# Extract the initial substring of $text that is bounded by
# an XML tag.

($extracted, $remainder) = extract_tagged($text);

# Extract the initial substring of $text that is bounded by
# a C<BEGIN>...C<END> pair. Don't allow nested C<BEGIN> tags

($extracted, $remainder) =
    extract_tagged($text,"BEGIN","END",undef,{bad=>["BEGIN"]});

# Extract the initial substring of $text that represents a
# Perl "quote or quote-like operation"

($extracted, $remainder) = extract_quotelike($text);

# Extract the initial substring of $text that represents a block
# of Perl code, bracketed by any of character(s) specified by $delim
# (where the string $delim contains one or more of '(){}[]<>').

($extracted, $remainder) = extract_codeblock($text,$delim);

# Extract the initial substrings of $text that would be extracted by
# one or more sequential applications of the specified functions
# or regular expressions

@extracted = extract_multiple($text,
                              [ \&extract_bracketed,
                                \&extract_quotelike,
                                \&some_other_extractor_sub,
                                qr/[xyz]*/,
                                'literal',
                              ]);

# Create a string representing an optimized pattern (a la Friedl)
# that matches a substring delimited by any of the specified characters
# (in this case: any type of quote or a slash)

$patstring = gen_delimited_pat(q{'"`/});

# Generate a reference to an anonymous sub that is just like extract_tagged
# but pre-compiled and optimized for a specific pair of tags, and
# consequently much faster (i.e. 3 times faster). It uses qr// for better
# performance on repeated calls.

$extract_head = gen_extract_tagged('<HEAD>','</HEAD>');
($extracted, $remainder) = $extract_head->($text);

描述

各种 extract_... 子例程可用于提取分隔的子字符串,可能在跳过指定的字符串前缀后。默认情况下,该前缀是可选的空格 (/\s*/),但您可以将其更改为任何您想要的 (见下文)。

要提取的子字符串必须出现在字符串变量的当前pos位置(如果未定义pos位置,则为索引零)。换句话说,extract_...子例程不会提取字符串中任何位置的子字符串的第一个出现(就像未锚定的正则表达式一样)。相反,它们提取出现在字符串中当前匹配位置的子字符串的出现(就像\G锚定的正则表达式一样)。

列表上下文中的一般行为

在列表上下文中,所有子例程都返回一个列表,前三个元素始终是

[0]

提取的字符串,包括指定的定界符。如果提取失败,则返回undef

[1]

输入字符串的剩余部分(即提取字符串后的字符)。失败时,将返回整个字符串。

[2]

跳过的前缀(即提取字符串之前的字符)。失败时,将返回undef

请注意,在列表上下文中,原始输入文本(第一个参数)的内容不会以任何方式修改。

但是,如果输入文本是在变量中传递的,则该变量的pos值将更新为指向提取文本后的第一个字符。这意味着在列表上下文中,各种子例程可以像正则表达式一样使用。例如

while ( $next = (extract_quotelike($text))[0] )
{
    # process next quote-like (in $next)
}

标量和空上下文中的通用行为

在标量上下文中,返回提取的字符串,该字符串已从输入文本中删除。因此,以下代码也处理每个类似引号的操作,但实际上将它们从 $text 中删除

while ( $next = extract_quotelike($text) )
{
    # process next quote-like (in $next)
}

请注意,如果输入文本是只读字符串(即文字),则不会尝试删除提取的文本。

在空上下文中,提取子例程的行为与标量上下文完全相同,只是(当然)没有返回提取的子字符串。

关于前缀的说明

前缀模式匹配不带任何尾随修饰符(/gimsox 等)。如果您期望像 '.*?(?=<H1>)' 这样的前缀规范跳过直到第一个 <H1> 标记的所有内容,这可能会让您感到困扰。这样的前缀模式只有在 <H1> 标记在当前行上时才会成功,因为 . 通常不匹配换行符。

为了克服此限制,您需要在前缀模式中打开 /s 匹配,使用 (?s) 指令:'(?s).*?(?=<H1>)'

函数

extract_delimited

extract_delimited 函数形式化了从字符串开头提取单个字符分隔子字符串的常见习惯用法。例如,要提取用单引号分隔的字符串,通常使用以下代码

($remainder = $text) =~ s/\A('(\\.|[^'])*')//s;
$extracted = $1;

但使用 extract_delimited 可以简化为

($extracted,$remainder) = extract_delimited($text, "'");

extract_delimited 最多接受四个标量(输入文本、分隔符、要跳过的前缀模式以及任何转义字符),并提取文本的初始子字符串,该子字符串被适当地分隔。如果分隔符字符串包含多个字符,则在文本中遇到的第一个字符被视为分隔子字符串。第三个参数指定要跳过的前缀模式(但必须存在!),然后提取子字符串。最后一个参数指定用于每个分隔符的转义字符。

所有参数都是可选的。如果未指定转义字符,则每个分隔符都使用反斜杠 (\) 转义。如果未指定前缀,则使用模式 '\s*' - 可选空格。如果分隔符集也未指定,则使用集 /["'`]/。如果要处理的文本也未指定,则使用 $_

在列表上下文中,extract_delimited 返回一个包含三个元素的数组,即提取的子字符串(包括周围的分隔符)、文本的其余部分以及跳过的前缀(如果有)。如果未找到合适的带分隔符的子字符串,则数组的第一个元素为空字符串,第二个元素是完整的原始文本,第三个元素中返回的前缀为空字符串。

在标量上下文中,只返回提取的子字符串。在空上下文中,提取的子字符串(以及任何前缀)只是从第一个参数的开头删除。

示例

# Remove a single-quoted substring from the very beginning of $text:

    $substring = extract_delimited($text, "'", '');

# Remove a single-quoted Pascalish substring (i.e. one in which
# doubling the quote character escapes it) from the very
# beginning of $text:

    $substring = extract_delimited($text, "'", '', "'");

# Extract a single- or double- quoted substring from the
# beginning of $text, optionally after some whitespace
# (note the list context to protect $text from modification):

    ($substring) = extract_delimited $text, q{"'};

# Delete the substring delimited by the first '/' in $text:

    $text = join '', (extract_delimited($text,'/','[^/]*')[2,1];

请注意,最后一个示例等同于删除第一个引号状模式。例如,如果 $text 包含字符串

"if ('./cmd' =~ m/$UNIXCMD/s) { $cmd = $1; }"

那么在删除之后,它将包含

"if ('.$UNIXCMD/s) { $cmd = $1; }"

而不是

"if ('./cmd' =~ ms) { $cmd = $1; }"

有关此问题的(部分)解决方案,请参阅 "extract_quotelike"

extract_bracketed

"extract_delimited" 一样,extract_bracketed 函数最多接受三个可选的标量参数:要从中提取的字符串、分隔符说明符和前缀模式。与之前一样,缺少的前缀默认为可选空格,缺少的文本默认为 $_。但是,缺少的分隔符说明符默认为 '{}()[]<>'(见下文)。

extract_bracketed 函数提取一个由平衡括号分隔的子字符串(使用用户指定的任何一个或多个分隔符括号:'(..)'、'{..}'、'[..]' 或 '<..>')。它可以选择性地尊重引号内的不平衡括号(见下文)。

“分隔符括号”是指作为 extract_bracketed 的第二个参数传递的分隔符列表中的括号。分隔符括号通过给出所需括号的左括号或右括号(或两者!)来指定。请注意,两个或多个分隔符括号的指定顺序并不重要。

“平衡括号分隔的子字符串”是指由匹配的括号包围的子字符串,使得子字符串内的任何其他(左或右)分隔符括号也由同一嵌套级别上的相反(右或左)分隔符括号匹配。任何不在分隔符列表中的括号类型都被视为普通字符。

换句话说,每个作为分隔符指定的括号类型都必须在子字符串内保持平衡并正确嵌套,并且子字符串中的任何其他类型的(“非分隔符”)括号都会被忽略。

例如,给定字符串

$text = "{ an '[irregularly :-(] {} parenthesized >:-)' string }";

然后在列表上下文中调用 extract_bracketed

@result = extract_bracketed( $text, '{}' );

将返回

( "{ an '[irregularly :-(] {} parenthesized >:-)' string }" , "" , "" )

因为两组 '{..}' 括号都正确嵌套且平衡。 (在标量上下文中,只返回数组的第一个元素。在空上下文中,$text 将被替换为空字符串。)

同样,在

@result = extract_bracketed( $text, '{[' );

中的调用将返回相同的结果,因为所有指定分隔符括号类型都正确嵌套且平衡。

但是,在

@result = extract_bracketed( $text, '{([<' );

中的调用将失败,返回

( undef , "{ an '[irregularly :-(] {} parenthesized >:-)' string }"  );

因为嵌入的 '(..)''[..]' 对是“交叉嵌套”的,并且嵌入的 '>' 不平衡。 (在标量上下文中,此调用将返回空字符串。在空上下文中,$text 将保持不变。)

请注意,字符串中的嵌入单引号在这种情况下没有帮助,因为它们没有被指定为可接受的分隔符,因此被视为非分隔符字符(并被忽略)。

但是,如果在分隔符规范中包含特定类型的引号字符,那么该类型的引号将被正确处理。例如,如果 $text

$text = '<A HREF=">>>>">link</A>';

然后

@result = extract_bracketed( $text, '<">' );

返回

( '<A HREF=">>>>">', 'link</A>', "" )

正如预期。如果没有指定"作为嵌入式引号

@result = extract_bracketed( $text, '<>' );

结果将是

( '<A HREF=">', '>>>">link</A>', "" )

除了引号分隔符'"`之外,还可以通过将字母“q”作为分隔符来指定完整的 Perl 引号式引号(即 q{string}、qq{string} 等)。因此

@result = extract_bracketed( $text, '<q>' );

将正确匹配类似这样的内容

$text = '<leftop: conj /and/ conj>';

另请参见:"extract_quotelike""extract_codeblock"

extract_variable

extract_variable 提取任何有效的 Perl 变量或包含变量的表达式,包括标量、数组、哈希、数组访问、哈希查找、通过对象进行方法调用、通过子例程引用进行子例程调用等。

该子例程最多接受两个可选参数

  1. 要处理的字符串(如果省略字符串或为undef,则为$_

  2. 指定要匹配的模式作为前缀的字符串(将跳过该模式)。如果省略,则跳过可选的空格。

在列表上下文中成功时,将返回一个包含 3 个元素的数组。这些元素是

[0]

提取的变量或类似变量的表达式

[1]

输入文本的其余部分,

[2]

前缀子字符串(如果有),

失败时,所有这些值(除了剩余文本)都为undef

在标量上下文中,extract_variable 只返回与类似变量的表达式匹配的完整子字符串。失败时返回undef。此外,原始输入文本已从其中删除返回的子字符串(以及任何前缀)。

在空上下文,输入文本只是删除了匹配的子字符串(以及任何指定的前缀)。

extract_tagged

extract_tagged 提取和分割(平衡)指定标签之间的文本。

该子例程最多接受五个可选参数

  1. 要处理的字符串(如果省略字符串或为undef,则为$_

  2. 指定要匹配的模式(即正则表达式)作为开始标签的字符串。如果省略模式字符串(或为undef),则使用匹配任何标准 XML 标签的模式。

  3. 指定要匹配的模式作为结束标签的字符串。如果省略模式字符串(或为undef),则结束标签是通过在实际匹配的开始标签(不是匹配标签的模式)中的任何前导括号字符后插入/来构造的。例如,如果开始标签模式指定为'{{\w+}}',并且实际匹配开始标签"{{DATA}}",则构造的结束标签将为"{{/DATA}}"

  4. 指定要匹配的模式作为前缀的字符串(将跳过该模式)。如果省略,则跳过可选的空格。

  5. 包含各种解析选项的哈希引用(见下文)

可以指定的各种选项是

reject => $listref

列表引用包含一个或多个字符串,指定在标记文本中不能出现的模式。

例如,要提取 HTML 链接(不应包含嵌套链接),请使用

extract_tagged($text, '<A>', '</A>', undef, {reject => ['<A>']} );
ignore => $listref

列表引用包含一个或多个字符串,指定被视为标记文本中嵌套标签的模式(即使它们与开始标签模式匹配)。

例如,要提取任意 XML 标签,但忽略“空”元素

extract_tagged($text, undef, undef, undef, {ignore => ['<[^>]*/>']} );

(另请参见下面的 "gen_delimited_pat")。

fail => $str

fail 选项指示在未遇到匹配的结束标签时要采取的操作(即在字符串结束之前或某些 reject 模式匹配之前)。默认情况下,无法匹配结束标签会导致 extract_tagged 立即失败。

但是,如果与 <reject> 关联的字符串值为“MAX”,则 extract_tagged 返回直到失败点的完整文本。如果字符串为“PARA”,则 extract_tagged 仅返回标签后的第一个段落(直到第一个为空或仅包含空格字符的行)。如果字符串为空,则恢复默认行为(即失败)。

例如,假设开始标签 "/para" 引入一个段落,然后继续直到下一个 "/endpara" 标签或遇到另一个 "/para" 标签

$text = "/para line 1\n\nline 3\n/para line 4";

extract_tagged($text, '/para', '/endpara', undef,
                        {reject => '/para', fail => MAX );

# EXTRACTED: "/para line 1\n\nline 3\n"

假设相反,如果找不到匹配的 "/endpara" 标签,则 "/para" 标签仅引用紧随其后的段落

$text = "/para line 1\n\nline 3\n/para line 4";

extract_tagged($text, '/para', '/endpara', undef,
                {reject => '/para', fail => MAX );

# EXTRACTED: "/para line 1\n"

请注意,指定的 fail 行为也适用于嵌套标签。

在列表上下文中成功时,将返回一个包含 6 个元素的数组。这些元素是

[0]

提取的标记子字符串(包括最外层的标签),

[1]

输入文本的其余部分,

[2]

前缀子字符串(如果有),

[3]

开始标签

[4]

开始标签和结束标签之间的文本

[5]

结束标签(如果未找到结束标签,则为 "")

失败时,所有这些值(除了剩余文本)都为undef

在标量上下文中,extract_tagged 仅返回与标记文本匹配的完整子字符串(包括开始和结束标签)。失败时返回 undef。此外,原始输入文本已从其中删除返回的子字符串(以及任何前缀)。

在空上下文,输入文本只是删除了匹配的子字符串(以及任何指定的前缀)。

gen_extract_tagged

gen_extract_tagged 生成一个新的匿名子例程,用于提取(平衡的)指定标签之间的文本。换句话说,它生成一个功能与 extract_tagged 相同的函数。

extract_taggedgen_extract_tagged 生成的匿名子例程之间的区别在于,这些生成的子例程

  • 不必在每次调用时重新解析标签规范或解析选项(而 extract_tagged 必须在每次调用时有效地重建其标签解析器);

  • 利用新的 qr// 结构预编译它们使用的正则表达式(而 extract_tagged 使用标准字符串变量插值来创建标签匹配模式)。

该子例程最多接受四个可选参数(与 extract_tagged 相同,除了要处理的字符串)。它返回一个对子例程的引用,该子例程又接受一个参数(要从中提取的文本)。

换句话说,extract_tagged 的实现与

sub extract_tagged
{
        my $text = shift;
        $extractor = gen_extract_tagged(@_);
        return $extractor->($text);
}

(尽管 extract_tagged 目前没有以这种方式实现)。

如果这些函数要被调用多次,使用 gen_extract_tagged 为特定标签创建提取函数是一个好主意,因为它们的性能通常比更通用的 extract_tagged 好两倍。

extract_quotelike

extract_quotelike 尝试识别、提取和分割各种 Perl 引号和类似引号的操作符(参见 perlop(3))。嵌套的反斜杠分隔符、嵌入的平衡括号分隔符(对于类似引号的操作符)和尾随修饰符都会被捕获。例如,在

extract_quotelike 'q # an octothorpe: \# (not the end of the q!) #'

extract_quotelike '  "You said, \"Use sed\"."  '

extract_quotelike ' s{([A-Z]{1,8}\.[A-Z]{3})} /\L$1\E/; '

extract_quotelike ' tr/\\\/\\\\/\\\//ds; '

中,所有完整的 Perl 类似引号操作都被正确提取。

还要注意,当在正则表达式上使用 /x 修饰符时,任何包含当前模式分隔符的注释都会导致正则表达式立即终止。换句话说

'm /
        (?i)            # CASE INSENSITIVE
        [a-z_]          # LEADING ALPHABETIC/UNDERSCORE
        [a-z0-9]*       # FOLLOWED BY ANY NUMBER OF ALPHANUMERICS
   /x'

将被提取为

'm /
        (?i)            # CASE INSENSITIVE
        [a-z_]          # LEADING ALPHABETIC/'

此行为与实际编译器的行为相同。

extract_quotelike 接受两个参数:要处理的文本和要在文本开头匹配的前缀。如果没有指定前缀,则默认值为可选的空格。如果没有给出文本,则使用 $_

在列表上下文中,返回一个包含 11 个元素的数组。这些元素是

[0]

提取的类似引号的子字符串(包括尾随修饰符),

[1]

输入文本的其余部分,

[2]

前缀子字符串(如果有),

[3]

引号类运算符的名称(如果有),

[4]

操作第一个块的左定界符,

[5]

操作第一个块的文本(即引号的内容、匹配或替换的正则表达式或翻译的目标列表),

[6]

操作第一个块的右定界符,

[7]

操作第二个块的左定界符(即如果是 stry),

[8]

操作第二个块的文本(即替换的替换内容或翻译的翻译列表),

[9]

操作第二个块的右定界符(如果有),

[10]

操作的尾随修饰符(如果有)。

对于标记为“(如果有)”的每个字段,成功时的默认值为一个空字符串。失败时,所有这些值(除了剩余文本)都为 undef

在标量上下文中,extract_quotelike 仅返回匹配引号类操作的完整子字符串(或在失败时返回 undef)。在标量或空上下文中,输入文本将删除相同的子字符串(以及任何指定的前缀)。

示例

# Remove the first quotelike literal that appears in text

        $quotelike = extract_quotelike($text,'.*?');

# Replace one or more leading whitespace-separated quotelike
# literals in $_ with "<QLL>"

        do { $_ = join '<QLL>', (extract_quotelike)[2,1] } until $@;


# Isolate the search pattern in a quotelike operation from $text

        ($op,$pat) = (extract_quotelike $text)[3,5];
        if ($op =~ /[ms]/)
        {
                print "search pattern: $pat\n";
        }
        else
        {
                print "$op is not a pattern matching operation\n";
        }
extract_quotelike

extract_quotelike 可以成功地从输入字符串中提取“here 文档”,但列表上下文中有一个重要的注意事项。

与其他类型的引号类文字不同,here 文档很少是连续的子字符串。例如,使用 here 文档的典型代码片段可能如下所示

<<'EOMSG' || die;
This is the message.
EOMSG
exit;

在标量上下文中将此作为输入字符串,extract_quotelike 将正确地返回字符串 "<<'EOMSG'\nThis is the message.\nEOMSG",并将字符串 " || die;\nexit;" 留在原始变量中。换句话说,here 文档的两个独立部分被成功提取并连接起来。

在列表上下文中,extract_quotelike 将返回列表

[0]

"<<'EOMSG'\nThis is the message.\nEOMSG\n"(即完整的提取的 here 文档,包括前后定界符),

[1]

" || die;\nexit;"(即输入文本的其余部分,连接起来),

[2]

""(即前缀子字符串 - 在这种情况下是微不足道的),

[3]

"<<"(即引号类运算符的“名称”),

[4]

"'EOMSG'"(即 here 文档的左定界符,包括任何引号),

[5]

"This is the message.\n"(即 here 文档的文本),

[6]

"EOMSG"(即此处文档的右分隔符),

[7..10]

""(此处文档没有第二个左分隔符、第二个文本、第二个右分隔符或尾随修饰符)。

但是,输入变量的匹配位置将设置为“exit;”(即在此处文档的结束分隔符之后),这将导致在任何代码片段提取序列中跳过早期的“ || die;\nexit;”。

为了避免此问题,当从可修改字符串中提取时遇到此处文档时,extract_quotelike 会静默地将字符串重新排列为等效的 Perl 代码片段

<<'EOMSG'
This is the message.
EOMSG
|| die;
exit;

其中此处文档是连续的。它仍然将匹配位置保留在此处文档之后,但现在不会跳过此处文档开始的行上的其余部分。

为了防止<extract_quotelike>以这种方式修改输入(这是列表上下文 extract_quotelike 唯一执行此操作的情况),您可以将输入变量作为插值文字传递

$quotelike = extract_quotelike("$var");
extract_codeblock

extract_codeblock 尝试识别并提取一个平衡的括号分隔的子字符串,该子字符串可能包含 Perl 引号或类似引号操作中的不平衡括号。也就是说,extract_codeblock 类似于 "extract_bracketed""extract_quotelike" 的组合。

extract_codeblock 接受与 extract_bracketed 相同的前三个参数:要处理的文本、要查找的一组分隔符括号和要首先匹配的前缀。它还接受一个可选的第四个参数,它允许分别指定最外层的分隔符括号(见下文),以及一个仅由 Parse::RecDescent 使用的第五个参数。

省略第一个参数(输入文本)表示改为处理 $_。省略第二个参数(分隔符括号)表示仅使用 '{'。省略第三个参数(前缀参数)表示开始时可选的空格。省略第四个参数(最外层分隔符括号)表示第二个参数的值将用于最外层分隔符。

一旦识别出前缀和最外层的起始分隔符括号,代码块就会通过遍历输入文本并按顺序尝试以下几种方法来提取。

  1. 尝试匹配一个结束分隔符括号。如果括号与最后一个起始括号的类型相同,则返回到该点的子字符串。如果括号不匹配,则返回错误。

  2. 尝试匹配一个引号或类似引号的操作符。如果找到,调用 extract_quotelike 来处理它。如果 extract_quotelike 失败,则返回它返回的错误。否则返回步骤 1。

  3. 尝试匹配一个起始分隔符括号。如果找到,递归调用 extract_codeblock 来处理嵌入的块。如果递归调用失败,则返回错误。否则,返回步骤 1。

  4. 无条件匹配一个裸字或任何其他单个字符,然后返回步骤 1。

示例

# Find a while loop in the text

        if ($text =~ s/.*?while\s*\{/{/)
        {
                $loop = "while " . extract_codeblock($text);
        }

# Remove the first round-bracketed list (which may include
# round- or curly-bracketed code blocks or quotelike operators)

        extract_codeblock $text, "(){}", '[^(]*';

在某些情况下,能够指定不同的最外层分隔符括号很有用。例如,在 Parse::RecDescent 模块中,仅在成功解析时执行的解析器操作使用 <defer:...> 指令指定。例如

sentence: subject verb object
                <defer: {$::theVerb = $item{verb}} >

Parse::RecDescent 使用 extract_codeblock($text, '{}<>') 来提取 <defer:...> 指令中的代码,但存在一个问题。

像这样的延迟操作

<defer: {if ($count>10) {$count--}} >

将被错误地解析为

<defer: {if ($count>

因为“小于”运算符被解释为结束分隔符。

但是,通过使用 extract_codeblock($text, '{}', undef, '<>') 提取指令,'>' 字符仅在代码块的最外层被视为分隔符,因此指令被正确解析。

extract_multiple

extract_multiple 子例程接受一个要处理的字符串和一个要应用于该字符串的提取器列表(子例程或正则表达式)。

在数组上下文中,extract_multiple 返回原始字符串的子字符串数组,这些子字符串由指定的提取器提取。在标量上下文中,extract_multiple 返回从原始字符串中成功提取的第一个子字符串。在标量和空上下文中,原始字符串都将第一个成功提取的子字符串从其中删除。在所有上下文中,extract_multiple 从字符串的当前 pos 开始,并在匹配后适当地设置该 pos

因此,在列表上下文中调用 extract_multiple 的目的是通过重复将每个指定的提取器应用于字符串的剩余部分,将处理后的字符串拆分为尽可能多的非重叠字段。因此,extract_multiple 是 Perl 的 split 子例程的通用形式。

该子例程最多接受四个可选参数

  1. 要处理的字符串(如果省略字符串或为undef,则为$_

  2. 一个指向子例程引用和/或 qr// 对象和/或文字字符串和/或哈希引用的列表的引用,指定用于拆分字符串的提取器。如果省略此参数(或为 undef),则列表

    [
            sub { extract_variable($_[0], '') },
            sub { extract_quotelike($_[0],'') },
            sub { extract_codeblock($_[0],'{}','') },
    ]

    被使用。

  3. 一个数字,指定要返回的最大字段数。如果省略此参数(或为 undef),则 split 会尽可能地继续。

    如果第三个参数为 N,则提取将继续进行,直到成功提取了 N 个字段,或直到字符串被完全处理。

    请注意,在标量和空上下文中,此参数的值会自动重置为 1(在 -w 下,如果必须重置参数,则会发出警告)。

  4. 一个值,指示是否应该跳过文本中未匹配的子字符串(见下文),或者将它们作为字段返回。如果该值为真,则跳过这些子字符串。否则,它们将被返回。

提取过程通过按顺序将每个提取器应用于文本字符串来进行。

如果提取器是一个子例程,则它在列表上下文中被调用,并期望返回一个包含单个元素的列表,即提取的文本。它还可以选择性地返回另外两个参数:一个字符串,表示提取后剩余的文本(类似于模式匹配的 $'),以及一个字符串,表示提取之前跳过的任何前缀(类似于模式匹配的 $`)。请注意,这旨在方便使用其他 Text::Balanced 子例程与 extract_multiple。还要注意,提取器子例程返回的值不必与原始文本的相应子字符串有任何关系(见下面的示例)。

如果提取器是一个预编译的正则表达式或一个字符串,则它在标量上下文中与文本匹配,并带有前导的 '\G' 和启用的 gc 修饰符。提取的值是 $1(如果该变量在匹配后已定义),否则是完整的匹配(即 $&)。

如果提取器是一个哈希引用,则它必须包含一个元素。该元素的值是上述提取器类型之一(子例程引用、正则表达式或字符串)。该元素的键是类的名称,提取器成功返回的值将被祝福到该类中。

如果提取器返回一个已定义的值,则该值将立即被视为下一个提取的字段,并被推入字段列表。如果提取器是在哈希引用中指定的,则该字段也会被祝福到相应的类中。

如果提取器无法匹配(对于正则表达式提取器),或者返回一个空列表或一个未定义的值(对于子例程提取器),则假定它未能提取。如果所有提取器子例程都未成功,则从文本开头提取一个字符,然后重新应用提取器子例程。这样删除的字符会被累积起来,最终成为下一个字段(除非第四个参数为真,在这种情况下它们会被丢弃)。

例如,以下代码提取有效的 Perl 变量的子字符串

@fields = extract_multiple($text,
                           [ sub { extract_variable($_[0]) } ],
                           undef, 1);

此示例将文本分成以引号分隔、花括号括起来以及其他任何内容的字段。分隔和括起来的部件也被赋予祝福以识别它们(“其他任何内容”没有祝福)。

@fields = extract_multiple($text,
           [
                { Delim => sub { extract_delimited($_[0],q{'"}) } },
                { Brack => sub { extract_bracketed($_[0],'{}') } },
           ]);

此调用提取下一个有效的 Perl 类引号运算符的单个子字符串(并将其从 $text 中删除)。

$quotelike = extract_multiple($text,
                              [
                                sub { extract_quotelike($_[0]) },
                              ], undef, 1);

最后,这里还有另一种方法来进行逗号分隔值解析。

$csv_text = "a,'x b',c";
@fields = extract_multiple($csv_text,
                          [
                                sub { extract_delimited($_[0],q{'"}) },
                                qr/([^,]+)/,
                          ],
                          undef,1);
# @fields is now ('a', "'x b'", 'c')

第二个参数中的列表表示:“尝试提取以 ' 或 " 分隔的字符串,否则提取直到逗号的任何内容...”。第三个参数为 undef 表示:“...尽可能多地提取...”,第四个参数中的真值表示:“...丢弃出现的任何其他内容(即逗号)”

如果您希望保留逗号作为单独的字段(即,如果您的分割模式具有捕获括号,则类似于 split 的行为),您只需将最后一个参数设置为未定义(或将其删除)。

gen_delimited_pat

gen_delimited_pat 子例程接受一个(字符串)参数,并构建一个 Friedl 风格的优化正则表达式,该正则表达式匹配以单个参数中的任何一个字符分隔的字符串。例如

gen_delimited_pat(q{'"})

返回正则表达式

(?:\"(?:\\\"|(?!\").)*\"|\'(?:\\\'|(?!\').)*\')

请注意,指定的定界符会自动进行 quotemeta 处理。

gen_delimited_pat 的典型用法是为 extract_tagged 构建专用标签。例如,要正确忽略“空”XML 元素(可能包含带引号的字符串)

my $empty_tag = '<(' . gen_delimited_pat(q{'"}) . '|.)+/>';

extract_tagged($text, undef, undef, undef, {ignore => [$empty_tag]} );

gen_delimited_pat 也可以调用一个可选的第二个参数,该参数指定用于每个定界符的“转义”字符。例如,要匹配 Pascal 风格的字符串(其中 ' 是定界符,'' 是字符串中的文字 ')

gen_delimited_pat(q{'},q{'});

可以为不同的定界符指定不同的转义字符。例如,要指定 '/' 是单引号的转义符,而 '%' 是双引号的转义符

gen_delimited_pat(q{'"},q{/%});

如果指定的定界符比转义字符多,则最后一个转义字符将用于剩余的定界符。如果未为给定的指定定界符指定转义字符,则使用 '\'。

delimited_pat

请注意,gen_delimited_pat 以前称为 delimited_pat。该名称可能仍然使用,但现在已弃用。

诊断信息

在列表上下文中,所有函数在失败时返回 (undef,$original_text)。在标量上下文中,失败通过返回 undef 来指示(在这种情况下,输入文本不会以任何方式修改)。

此外,在任何上下文中失败时,都会设置 $@ 变量。访问 $@->{error} 返回以下列出的错误诊断之一。访问 $@->{pos} 返回检测到错误的原始字符串中的偏移量(尽管不一定是在错误发生的位置!)。直接打印 $@ 会生成错误消息,并在后面附加偏移量。成功时,$@ 变量保证为 undef

可用的诊断信息是

未找到合适的括号:"%s"

提供给 extract_bracketed 的分隔符不是 '()[]<>{}' 中的任何一个。

未找到前缀:/%s/

指定了非可选前缀,但未在文本开头找到。

在前缀后未找到左括号:"%s"

extract_bracketedextract_codeblock 正在文本开头期待特定类型的括号,但未找到。

在前缀后未找到类似引号的操作符:"%s"

extract_quotelike 未在它正在提取的子字符串开头找到类似引号的操作符 qqqqwqxstry 之一。

不匹配的右括号:"%c"

extract_bracketedextract_quotelikeextract_codeblock 在预期之外的位置遇到了右括号。

不匹配的左括号(s):"%s"

extract_bracketedextract_quotelikeextract_codeblock 在关闭一个或多个嵌套括号级别之前用尽了文本中的字符。

不匹配的嵌入式引号 (%s)

extract_bracketed 尝试匹配嵌入的引号子字符串,但未能找到匹配的右引号。

未找到与 '%s' 匹配的右分隔符

extract_quotelike 无法找到与打开引号操作匹配的结束分隔符。

不匹配的结束括号:预期为 "%c",但找到 "%s"

extract_bracketedextract_quotelikeextract_codeblock 找到了有效的括号分隔符,但它不是正确的类型。这通常表示嵌套错误,但也可能表示引号或转义不正确。

在引号 "%s" 后未找到块分隔符

extract_quotelikeextract_codeblock 找到了引号运算符 qqqqwqxstry 之一,但其后没有合适的块。

未找到前导解引用符

extract_variable 预期在变量的开头出现 '$'、'@' 或 '%' 之一,但没有找到任何一个。

解引用符后的标识符无效

extract_variable 找到了表示变量的 '$'、'@' 或 '%',但该字符后面没有合法的 Perl 标识符。

在 %s 处未找到预期的开始括号

extract_codeblock 无法找到指定的任何最外层开始括号。

在 %s 处嵌套代码块不正确

找到了一个嵌套的代码块,它以指定为仅用作最外层括号的分隔符开始。

引号 "%s" 缺少第二个块

extract_codeblockextract_quotelike 找到了引号运算符 stry 之一,但其后只有一个块。

未找到与开始括号匹配的项

extract_codeblock 无法找到与最外层开始括号匹配的结束括号。

未找到开始标签:/%s/

extract_tagged 未找到合适的开始标签(在删除任何指定的前缀后)。

无法构建与 /%s/ 匹配的结束标签

extract_tagged 匹配了指定的开始标签,并尝试修改匹配的文本以生成匹配的结束标签(因为没有指定结束标签)。它无法生成结束标签,几乎可以肯定是因为开始标签没有以某种括号开头。

找到无效的嵌套标签:%s

extract_tagged 发现了一个嵌套标签,该标签出现在“拒绝”列表中(并且失败模式不是“MAX”或“PARA”)。

发现不平衡的嵌套标签:%s

extract_tagged 发现了一个嵌套的开始标签,它没有与相应的嵌套结束标签匹配(并且失败模式不是“MAX”或“PARA”)。

未找到结束标签

extract_tagged 在到达文本末尾时,没有找到与原始开始标签匹配的结束标签(并且失败模式不是“MAX”或“PARA”)。

EXPORTS

以下符号是或可以被此模块导出

默认导出

.

可选导出

extract_delimited, extract_bracketed, extract_quotelike, extract_codeblock, extract_variable, extract_tagged, extract_multiple, gen_delimited_pat, gen_extract_tagged, delimited_pat.

导出标签
:ALL

extract_delimited, extract_bracketed, extract_quotelike, extract_codeblock, extract_variable, extract_tagged, extract_multiple, gen_delimited_pat, gen_extract_tagged, delimited_pat.

已知错误

参见 https://rt.cpan.org/Dist/Display.html?Status=Active&Queue=Text-Balanced.

反馈

欢迎补丁、错误报告、建议或任何其他反馈。

补丁可以作为 GitHub 拉取请求发送到 https://github.com/steve-m-hay/Text-Balanced/pulls.

错误报告和建议可以在 CPAN 请求跟踪器上提出,地址为 https://rt.cpan.org/Public/Bug/Report.html?Queue=Text-Balanced.

目前在 CPAN 请求跟踪器上的活动请求可以在 https://rt.cpan.org/Public/Dist/Display.html?Status=Active;Queue=Text-Balanced 查看。

请测试此发行版。有关如何参与的详细信息,请参阅 CPAN 测试人员报告,地址为 https://www.cpantesters.org/

可以在 CPAN 测试人员报告中查看以前的测试结果,地址为 https://www.cpantesters.org/distro/T/Text-Balanced.html.

请在 CPAN 评级中对本发行版进行评级,地址为 https://cpanratings.perl.org/rate/?distribution=Text-Balanced.

可用性

此模块的最新版本可从 CPAN 获取(有关详细信息,请参阅 "CPAN" in perlmodlib),地址为

https://metacpan.org/release/Text-Balanced

https://www.cpan.org/authors/id/S/SH/SHAY/

https://www.cpan.org/modules/by-module/Text/.

最新的源代码可在 GitHub 上获取:https://github.com/steve-m-hay/Text-Balanced

安装

请参阅 INSTALL 文件。

作者

Damian Conway <[email protected]>。

Steve Hay <[email protected]> 从 2.03 版本开始维护 Text::Balanced。

版权

版权所有 (C) 1997-2001 Damian Conway。保留所有权利。

版权所有 (C) 2009 Adam Kennedy。

版权所有 (C) 2015, 2020, 2022 Steve Hay 和其他贡献者。保留所有权利。

许可证

本模块是自由软件;您可以根据与 Perl 本身相同的条款重新发布和/或修改它,即根据 GNU 通用公共许可证或 Artistic 许可证的条款,如 LICENCE 文件中所述。

版本

版本 2.06

日期

2022 年 6 月 5 日

历史

请参阅 Changes 文件。