簡體   English   中英

使用perl中的regex從字符串中提取子字符串?

[英]extract substring from a string using regex in perl?

嘗試提取匹配string中pattern的子串。 例如,我有類似下面的文本

[ Pierre/NNP Vinken/NNP ]
,/, 
[ 61/CD years/NNS ]
old/JJ ,/, will/MD join/VB 
[ the/DT board/NN ]
as/IN 
[ a/DT nonexecutive/JJ director/NN Nov./NNP 29/CD ]
./. 
[ Mr./NNP Vinken/NNP ]
is/VBZ 
[ chairman/NN ]
of/IN 

我想在斜杠(/)和斜杠之后提取任何東西,但不知何故,我的正則表達式提取第一個子字符串並忽略該行中的其余子字符串。

我的輸出如下所示:

tag:Pierre/NNP Vinken - word:Pierre/NNP Vinken/NNP ->1
tag:, - word:,/, ->1
tag:61/CD years - word:61/CD years/NNS ->1
tag:old/JJ ,/, will/MD join - word:old/JJ ,/, will/MD join/VB ->1
tag:the/DT board - word:the/DT board/NN ->1
tag:as - word:as/IN ->1
tag:a/DT nonexecutive/JJ director/NN Nov./NNP 29 - word:a/DT nonexecutive/JJ director/NN Nov./NNP 29/CD ->1
tag:. - word:./. ->1
tag:Mr./NNP Vinken - word:Mr./NNP Vinken/NNP ->1
tag:is - word:is/VBZ ->1
tag:chairman - word:chairman/NN ->1
tag:of - word:of/IN ->1

但我真正想要的是下面這樣的東西

tag:NNP  - word:Pierre ->1
tag:NNP  - word:Vinken ->1
tag:,    - word:,      ->1
tag:CD   - word:61     ->1
.
.
etc.

我使用的代碼:

    while (my $line = <$fh>) {
        chomp $line;
        #remove square brackets
        $line=~s/[\[\]]//;

        while($line =~m/((\s*(.*))\/((.*)\s+))/gi)
        {
            $word=$1;
            $tag=$2;
            #remove whitespace from left and right of string
            $word=~ s/^\s+|\s+$//g;
            $tag=~ s/^\s+|\s+$//g;
            $tags{$tag}++;
            $tagHash{$tag}{$word}++;
        }

    }
foreach my $str (sort keys %tagHash)
{
    foreach my $s (keys %{$tagHash{$str}} )
    {
        print "tags:$str - word: $s-> $tagHash{$str}{$s}\n";
    }
}

任何想法為什么我的正則表達式不應該表現出來

編輯:

在我正在解析的文本文件中也有野性字符和標點符號,這意味着文件將具有如下內容:''/''“/”,/,。/。 ?/? !/! 等等

所以我想要捕獲所有這些東西,不僅僅是字母和數字字符。

我認為你有tag/word s tagword可能是一切,除了一些字符,如],[,\\s, ,:

\s*([^\[\]\s]+?)\/([^\[\]\s]+)\s*
    ^^^^^^^^^1

此正則表達式與您的原始模式類似。 (見DEMO

描述:

1-此捕獲組匹配每個角色. 那不是[]\\s

圍繞整個模式的最外面一組括號被捕獲到$1 ,這顯然不是預期的。 此外, .*\\/的貪婪意味着它需要一切到最后 / 同樣, .*\\s+只留下最后一個空格。

一種方法是使用否定的字符類

my ($word, $tag) = m{ ([^/\s]+) / ([^/\s]+) }x;

模式[^/\\s]+匹配一個或多個連續字符的字符串,每個字符不是/或空格。 所以你在/之前和之后得到一個“ 單詞 ”。 如果你采取“ 斜線后的任何東西 ”,正如文字所說,不清楚在下一次斜線之前應該是什么。

然后你的方法就可以了

while (my $line = <$fh>) 
{
    while ( $line =~ m{ ([^/\s]+) / ([^/\s]+) }gx )
    {
        $tagHash{$2}{$1}++;
    }
}

另一個計數似乎無關緊要,所以我把它留下來專注於這個問題。


但是,這里有一點點缺失。

此方法無法檢測線條何時與預期格式不同。 例如

word1/tag1 word2/tag2/ tag3/word4/tag4

安靜地產生錯誤的結果。 一些違規行為被忽略,但有許多不良案例。

捕獲這一點的一種方法是預處理該行,檢查所有斜杠之間至少有兩個單詞,並且在第一個和最后一個之后至少有一個單詞。 這意味着每行處理兩次,而且它也變得更加混亂。 例如

while (my $line = <$fh>) 
{
    my @parts = split '/', $line;
    if (not shift @parts or not pop @parts or grep { 2 > split } @parts) {
        warn "Unexpected format: $line";
        next;
    }

    $tagHash{$2}{$1}++  while $line =~ m{ ([^/\s]+) / ([^/\s]+) }gx;
}

此檢查會更改@parts數組,因此如果以后需要該數組,則可以更好地使用

if (!$parts[0] or !$parts[-1] or grep { 2 > split } @parts[1..@parts-2])  { ...

而不是grep也可以使用List :: Util中的 any短路

另一種方法是改變方法,仔細解析線,而不是盲目地跳過正則表達式匹配。 由於第一個和最后一個可能只有一個單詞,這可能很難用正則表達式。 分割和使用數組可能更清晰,更實用。

很難想象格式總是匹配數據,所以我建議考慮一些。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM