根据POS标签写入否定字符串

时间:2012-02-12 15:34:30

标签: php regex nlp regex-negation

考虑以下带有POS标签的字符串:

It/PRP was/VBD not/RB okay/JJ or/CC funny/JJ and/CC I/NN will/MD never/RB buy/VB 
from/IN them/PRP ever/RB again/RB

(It was not okay or funny and I will never buy from them ever again)

我想完成以下任务:

  1. 检查否定副词(RB)与定义的数组('not','never')
  2. 如果匹配,请删除副词
  3. 将“not-”连接到每个后续形容词(JJ),副词(RB)或动词(过去时的VB或VBN)的开头
  4. 删除所有POS标签(/ XX)
  5. 因此,所需的输出将是:

    It was not-okay or not-funny and I will not-buy from them not-ever not-again
    

    我的第一个想法是以我知道的方式做到这一点:在空间上爆炸字符串,然后将“/”上的每个单词分解为[JJ =>好的],然后制作一个switch语句来处理每个单词(例如JJ:连接等),但这看起来很草率。有没有人有更干净和/或更有效的方法,例如正则表达式?字符串已经过预先清理,所以它们总是只包含单词(没有标点符号,其他字符比a-z等)。

    编辑:我知道,顺便说一下这种对待否定的方式的基本特征,但它足以满足我的需要。会有错误余量,但没关系:)

2 个答案:

答案 0 :(得分:1)

让我试试。关于使用正则表达式进行处理的问题,

$s = "It/PRP was/VBD not/RB okay/JJ or/CC funny/JJ and/CC I/NN will/MD 
      never/RB buy/VB from/IN them/PRP ever/RB again/RB";
  1. 检查否定副词(RB)与定义的数组('not','never')
  2. 如果匹配,请删除副词

    换句话说,删除任何“not”和“never”后跟“/ RB”。

    $s = preg_replace("/(not|never)\/RB/i", "", $s);
    
  3. 将“not-”连接到每个后续形容词(JJ),副词(RB)或动词(过去时的VB或VBN)的开头

    $s = preg_replace("/(\w+)\/(JJ|RB|VB|VBN)/", "not-$1/$2", $s);
    
  4. 删除所有POS标签(/ XX)

    假设POS标签全部为大写

    $s = preg_replace("/\/[A-Z]+/", "", $s);
    
  5. (我的附加步骤)。从上面的正则表达式的结果中删除任何双重空格。

    $s = preg_replace("/\s+/", " ", $s);
    
  6. 输出结果为:

    It not-was not-okay or not-funny and I will not-buy from them not-ever not-again
    

    如果你想否定除JJ / RB / VB / VBN之外的其他POS标签,只需修改步骤3(JJ | RB | VB | VBN)上的正则表达式。希望它有所帮助。

答案 1 :(得分:0)

我建议使用非正则表达式解决方案,因为正则表达式或非正则表达式方法需要进行多个函数调用。如果有一种方法可以用一个简单的preg_replace_callback()来执行这项任务,我只能想象乍看起来很难理解。我的方法可能与你的原始代码非常相似(但我不知道,因为你没有发布你的代码)。

方法:(Demo

$string='It/PRP was/VBD not/RB okay/JJ or/CC funny/JJ and/CC I/NN will/MD never/RB buy/VB from/IN them/PRP ever/RB again/RB';

$mods=['JJ','RB','VB','VBN'];  // okay to prefix when called for
$omits=['not','never'];  // strings to be omitted every time
$negbool=false;  // states whether a negative adverb has been found
$array=explode(' ',$string);
foreach($array as $k=>&$v){  // make $v modifiable by reference
    $parts=explode('/',$v);
    // add prefix "not-" to strings who qualify
    if($negbool && in_array($parts[1],$mods) && !in_array($parts[0],$omits)){
        $v="not-{$parts[0]}";
    // omit RB strings that don't qualify for prefixing
    }elseif($parts[1]=='RB'){
        unset($array[$k]);  // remove
        $negbool=true;  // declare that modifiable strings should be modified from this point forward
    // keep only leading substring
    }else{
        $v=$parts[0];
    }
}
echo implode(' ',$array); // glue back together again using spaces

输出:

It was not-okay or not-funny and I will not-buy from them not-ever not-again