內容

名稱

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]

提取的 quotelike 子字串(包括尾隨修飾符),

[1]

輸入文字的剩餘部分,

[2]

前綴子字串(如果有),

[3]

quotelike 算子名稱(如果有),

[4]

運算第一個區塊的左分隔符,

[5]

運算第一個區塊的文字(也就是引號的內容、匹配或替換的正則表達式或翻譯的目標清單),

[6]

運算第一個區塊的右分隔符,

[7]

運算第二個區塊的左分隔符(也就是如果它是 stry),

[8]

運算第二個區塊的文字(也就是替換的替換或翻譯的翻譯清單),

[9]

運算第二個區塊的右分隔符(如果有),

[10]

運算的尾隨修飾符(如果有)。

對於標記為「(如果有)」的每個欄位,成功時的預設值為空字串。失敗時,所有這些值(除了剩餘文字)都是 undef

在標量內容中,extract_quotelike 僅傳回與 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;" 保留在原始變數中。換句話說,這裡文件中的兩個獨立部分已成功提取並串接。

在清單內容中,extract_quotelike 會傳回清單

[0]

"<<'EOMSG'\nThis is the message.\nEOMSG\n"(即完整提取的這裡文件,包括前後分隔符號),

[1]

" || die;\nexit;"(即輸入文字的其餘部分,已串接),

[2]

""(即前置字串——此情況中很明顯),

[3]

"<<"(即引號運算子的「名稱」)

[4]

"'EOMSG'"(即這裡文件的左分隔符號,包括任何引號),

[5]

"This is the message.\n"(即這裡文件的文字),

[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 個欄位,或直到字串已完全處理完畢。

    請注意,在純量和 void 環境中,此引數的值會自動重設為 1(在 -w 下,如果必須重設引數,則會發出警告)。

  4. 一個值,用於指示文字中的不匹配子字串(見下文)是否應略過或作為欄位傳回。如果此值為 true,則會略過此類子字串。否則,則會傳回。

萃取程序的運作方式是依序將每個萃取器套用至文字字串。

如果萃取器是子常式,則會在清單環境中呼叫它,並預期它會傳回單一元素的清單,也就是萃取的文字。它也可以選擇傳回兩個額外的引數:一個代表萃取後所剩文字的字串(例如樣式比對的 $'),以及一個代表萃取前略過的任何前置字串(例如樣式比對的 $`)。請注意,此設計是用於協助將其他 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')

第二個引數中的清單表示:「嘗試萃取以 ' 或 " 分隔的字串,否則萃取任何內容直到逗號...」。未定義的第三個引數表示:「...盡可能多次...」,而第四個引數中的真值表示「...捨棄出現的任何其他內容(即逗號)」

如果你想要保留逗號作為個別欄位(即如果你的分隔模式有擷取括弧,則會像 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"

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」)。

匯出

這個模組匯出或可以匯出下列符號

預設匯出

.

選用匯出

extract_delimitedextract_bracketedextract_quotelikeextract_codeblockextract_variableextract_taggedextract_multiplegen_delimited_patgen_extract_taggeddelimited_pat

匯出標籤
:ALL

extract_delimitedextract_bracketedextract_quotelikeextract_codeblockextract_variableextract_taggedextract_multiplegen_delimited_patgen_extract_taggeddelimited_pat

已知錯誤

請參閱 https://rt.cpan.org/Dist/Display.html?Status=Active&Queue=Text-Balanced

回饋

歡迎提供修補程式、錯誤回報、建議或任何其他回饋。

修補程式可以透過 https://github.com/steve-m-hay/Text-Balanced/pulls 的 GitHub pull request 傳送。

錯誤回報和建議可以在 https://rt.cpan.org/Public/Bug/Report.html?Queue=Text-Balanced 的 CPAN 要求追蹤器提出。

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

請測試這個套件。請參閱 https://www.cpantesters.org/ 的 CPAN 測試人員報告,以取得如何參與的詳細資料。

先前在 CPAN 測試人員報告上的測試結果可以在 https://www.cpantesters.org/distro/T/Text-Balanced.html 查看。

請在 https://cpanratings.perl.org/rate/?distribution=Text-Balanced 的 CPAN 評分中評分此發行版。

取得方式

此模組的最新版本可從 CPAN 取得(詳細資訊請參閱 perlmodlib 中的「CPAN」),網址為

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 <damian@conway.org>。

Steve Hay <shay@cpan.org> 自 2.03 版起維護 Text::Balanced。

版權

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

版權所有 (C) 2009 Adam Kennedy。

版權所有 (C) 2015、2020、2022 Steve Hay 及其他貢獻者。保留所有權利。

授權

此模組為自由軟體;您可以根據 Perl 本身的條款重新散布或修改它,亦即根據 LICENCE 檔案中指定的 GNU 通用公共授權或 Artistic 授權條款。

版本

版本 2.06

日期

2022 年 6 月 5 日

歷史記錄

請參閱 Changes 檔案。