使用正则表达式将字符串拆分为句子

2023-12-11

我有随机文本存储在$sentences。使用正则表达式,我想将文本分成句子,请参阅:

function splitSentences($text) {
    $re = '/                # Split sentences on whitespace between them.
        (?<=                # Begin positive lookbehind.
          [.!?]             # Either an end of sentence punct,
        | [.!?][\'"]        # or end of sentence punct and quote.
        )                   # End positive lookbehind.
        (?<!                # Begin negative lookbehind.
          Mr\.              # Skip either "Mr."
        | Mrs\.             # or "Mrs.",
        | T\.V\.A\.         # or "T.V.A.",
                            # or... (you get the idea).
        )                   # End negative lookbehind.
        \s+                 # Split on whitespace between sentences.
        /ix';

    $sentences = preg_split($re, $text, -1, PREG_SPLIT_NO_EMPTY);
    return $sentences;
}

$sentences = splitSentences($sentences);

print_r($sentences);

效果很好。

但是,如果存在 unicode 字符,它不会分成句子:

$sentences = 'Entertainment media properties. Fairy Tail and Tokyo Ghoul.';

或者这个场景:

$sentences = "Entertainment media properties.&Acirc;&nbsp; Fairy Tail and Tokyo Ghoul.";

当文本中存在 unicode 字符时,我该怎么做才能使其工作?

这是一个ideone供测试用。

赏金信息

我正在寻找一个完整的解决方案。在发布答案之前,请阅读我与 WiktorStribiżew 的评论主题,了解有关此问题的更多相关信息。


正如预期的那样,任何类型的自然语言处理都不是一项微不足道的任务。原因是它们是进化系统。没有人坐下来思考哪些是好主意,哪些不是。每条规则都有 20-40% 的例外情况。话虽如此,可以执行您的命令的单个正则表达式的复杂性将是惊人的。尽管如此,以下解决方案主要依赖于正则表达式。


  • 这个想法是逐步回顾text.
  • 在任何给定时间,当前块text将包含在两个不同的部分中。一、候选子串before一个句子边界和另一个 -after.
  • 前 10 个正则表达式对检测看起来像句子边界但实际上不是的位置。在这种情况下,before and after无需注册新句子即可前进。
  • 如果这些对均不匹配,则将尝试与最后 3 对进行匹配,可能会检测到边界。

As for 这些正则表达式从哪里来?- 我翻译了这个 Ruby 库,它是根据生成的这张纸。如果你真的想理解它们,除了阅读论文别无选择。

就准确性而言 - 我鼓励您使用不同的文本进行测试。经过一番尝试后,我感到非常惊喜。

就性能而言 - 正则表达式应该具有高性能,因为它们都具有\A or \Z锚点,几乎没有重复量词,并且在有重复量词的地方 - 不能有任何回溯。不过,正则表达式就是正则表达式。如果您计划在大块文本上使用紧密循环,则必须进行一些基准测试。


强制性免责声明: 请原谅我生疏的 php 技能。下面的代码可能不是最惯用的 php,但它仍然应该足够清晰以表达要点。


function sentence_split($text) {
    $before_regexes = array('/(?:(?:[\'\"„][\.!?…][\'\"”]\s)|(?:[^\.]\s[A-Z]\.\s)|(?:\b(?:St|Gen|Hon|Prof|Dr|Mr|Ms|Mrs|[JS]r|Col|Maj|Brig|Sgt|Capt|Cmnd|Sen|Rev|Rep|Revd)\.\s)|(?:\b(?:St|Gen|Hon|Prof|Dr|Mr|Ms|Mrs|[JS]r|Col|Maj|Brig|Sgt|Capt|Cmnd|Sen|Rev|Rep|Revd)\.\s[A-Z]\.\s)|(?:\bApr\.\s)|(?:\bAug\.\s)|(?:\bBros\.\s)|(?:\bCo\.\s)|(?:\bCorp\.\s)|(?:\bDec\.\s)|(?:\bDist\.\s)|(?:\bFeb\.\s)|(?:\bInc\.\s)|(?:\bJan\.\s)|(?:\bJul\.\s)|(?:\bJun\.\s)|(?:\bMar\.\s)|(?:\bNov\.\s)|(?:\bOct\.\s)|(?:\bPh\.?D\.\s)|(?:\bSept?\.\s)|(?:\b\p{Lu}\.\p{Lu}\.\s)|(?:\b\p{Lu}\.\s\p{Lu}\.\s)|(?:\bcf\.\s)|(?:\be\.g\.\s)|(?:\besp\.\s)|(?:\bet\b\s\bal\.\s)|(?:\bvs\.\s)|(?:\p{Ps}[!?]+\p{Pe} ))\Z/su',
        '/(?:(?:[\.\s]\p{L}{1,2}\.\s))\Z/su',
        '/(?:(?:[\[\(]*\.\.\.[\]\)]* ))\Z/su',
        '/(?:(?:\b(?:pp|[Vv]iz|i\.?\s*e|[Vvol]|[Rr]col|maj|Lt|[Ff]ig|[Ff]igs|[Vv]iz|[Vv]ols|[Aa]pprox|[Ii]ncl|Pres|[Dd]ept|min|max|[Gg]ovt|lb|ft|c\.?\s*f|vs)\.\s))\Z/su',
        '/(?:(?:\b[Ee]tc\.\s))\Z/su',
        '/(?:(?:[\.!?…]+\p{Pe} )|(?:[\[\(]*…[\]\)]* ))\Z/su',
        '/(?:(?:\b\p{L}\.))\Z/su',
        '/(?:(?:\b\p{L}\.\s))\Z/su',
        '/(?:(?:\b[Ff]igs?\.\s)|(?:\b[nN]o\.\s))\Z/su',
        '/(?:(?:[\"”\']\s*))\Z/su',
        '/(?:(?:[\.!?…][\x{00BB}\x{2019}\x{201D}\x{203A}\"\'\p{Pe}\x{0002}]*\s)|(?:\r?\n))\Z/su',
        '/(?:(?:[\.!?…][\'\"\x{00BB}\x{2019}\x{201D}\x{203A}\p{Pe}\x{0002}]*))\Z/su',
        '/(?:(?:\s\p{L}[\.!?…]\s))\Z/su');
    $after_regexes = array('/\A(?:)/su',
        '/\A(?:[\p{N}\p{Ll}])/su',
        '/\A(?:[^\p{Lu}])/su',
        '/\A(?:[^\p{Lu}]|I)/su',
        '/\A(?:[^p{Lu}])/su',
        '/\A(?:\p{Ll})/su',
        '/\A(?:\p{L}\.)/su',
        '/\A(?:\p{L}\.\s)/su',
        '/\A(?:\p{N})/su',
        '/\A(?:\s*\p{Ll})/su',
        '/\A(?:)/su',
        '/\A(?:\p{Lu}[^\p{Lu}])/su',
        '/\A(?:\p{Lu}\p{Ll})/su');
    $is_sentence_boundary = array(false, false, false, false, false, false, false, false, false, false, true, true, true);
    $count = 13;

    $sentences = array();
    $sentence = '';
    $before = '';
    $after = substr($text, 0, 10);
    $text = substr($text, 10);

    while($text != '') {
        for($i = 0; $i < $count; $i++) {
            if(preg_match($before_regexes[$i], $before) && preg_match($after_regexes[$i], $after)) {
                if($is_sentence_boundary[$i]) {
                    array_push($sentences, $sentence);
                    $sentence = '';
                }
                break;
            }
        }

        $first_from_text = $text[0];
        $text = substr($text, 1);
        $first_from_after = $after[0];
        $after = substr($after, 1);
        $before .= $first_from_after;
        $sentence .= $first_from_after;
        $after .= $first_from_text;
    }

    if($sentence != '' && $after != '') {
        array_push($sentences, $sentence.$after);
    }

    return $sentences;
}

$text = "Mr. Entertainment media properties. Fairy Tail 3.5 and Tokyo Ghoul.";
print_r(sentence_split($text));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用正则表达式将字符串拆分为句子 的相关文章

  • Laravel 5 配置语言环境,不起作用

    已完成的修改 on 配置 应用程序 php locale gt env APP LOCALE en fallback locale gt en on env APP LOCALE pt 我也复制了 资源 lang en文件到 资源 lang
  • CodeIgniter/PHP - 从视图内调用视图

    基本上 对于我的网络应用程序 我正在尝试更好地组织它 目前 每次我想加载页面时 我都必须从控制器中执行此操作 如下所示 this gt load gt view subviews template headerview this gt lo
  • 如何在 SQLite 中获取最后插入的 ID?

    SQLite 中是否有任何内置函数可以获取最后插入的行 ID 例如 在 mysql 中我们有LAST INSERT ID 这种功能 对于 sqlite 任何可用于执行相同过程的函数 请帮我 Thanks SQLite 这可以使用SQLite
  • 如果端口不是 80,.htaccess 重定向到错误页面

    我正在运行一个便携式服务器 http www server2go web de 通过 USB 棒 问题是我还在我的本地计算机上安装了 WAMP 并且 Apache 不知何故在 Windows 启动时启动 因为一些我现在不记得的随机原因并且无
  • PHP正则表达式替换链接url

    我需要添加href 之前http 如果这http 不遵循href or src 以下代码部分有效 部分意味着它考虑 a href 只但不src a s i a href target blank gt 0 a str 预先感谢各位的回复 s
  • json_decode 到自定义类

    是否可以将 json 字符串解码为 stdClass 以外的对象 不是自动的 但你可以按照老式的路线来做 data json decode json true class new Whatever foreach data as key g
  • CakePHP Unfilled 单选按钮在提交时更改为不需要的值

    我有这个表单元素 form gt input ChecklistResponseGovernmentInfo driversLicenseIsOnline array type gt radio empty gt true options
  • 将 jQuery 与 Selenium WebDriver 结合使用 - 如何将 JSON 对象转换为 WebElement?

    我正在使用 Selenium WebDriver 我想执行 jQuery 代码来查找一些元素 我的代码如下 public function uploadGrantDoc script return itemlist grant file u
  • 使用正则表达式解析 Snort 警报文件

    我正在尝试使用 Python 中的正则表达式从 snort 警报文件中解析出源 目标 IP 和端口 和时间戳 示例如下 03 09 14 10 43 323717 1 2008015 9 ET MALWARE User Agent Win9
  • 正则表达式在 Velocity 模板中不起作用

    我在 Test java 中尝试过这个 String regex lt s br s s gt String test1 lt br gt System out println test replaceAll regex 但是当我在速度模板
  • zend框架验证模型中的数据而不是表单中的数据

    使用 Zend Framework 2 在我的应用程序中 要编辑数据库中的数据 可以编译 html 表单或发送 http post 请求 我的服务器作为 Web 服务实现 在第二种情况下 不会呈现表单 问题 如果当服务器收到不是从表单发送而
  • 在 jQuery 选择器上使用正则表达式查找基于 ids 的所有元素

    我有几个具有唯一 id 的元素 如下所示 div div div div div div 我希望使用 jQuery 可以实现以下功能 item top each function this hide 我对正则表达式没有很好的掌握 希望得到一
  • 如何验证上传的文件是视频?

    我的服务器上有一些非常敏感的信息 因此安全性是一个大问题 用户需要能够上传视频 我知道允许用户上传文件会带来安全威胁 因为没有 100 的方法可以阻止他们上传非视频 但我显然可以选择服务器将保留哪些文件 我知道检查文件扩展名是不够的 检查
  • 将 Base64 字符串转换为图像文件? [复制]

    这个问题在这里已经有答案了 我正在尝试将我的 Base64 图像字符串转换为图像文件 这是我的 Base64 字符串 http pastebin com ENkTrGNG http pastebin com ENkTrGNG 使用以下代码将
  • FastText - 由于 C++ 扩展未能分配内存,无法加载 model.bin

    我正在尝试使用 FastText Python APIhttps pypi python org pypi fasttext https pypi python org pypi fasttext虽然 据我所知 此 API 无法加载较新的
  • 安全地评估简单的数学

    我想知道是否有一种安全的方法来评估数学 例如 2 2 10000 12000 10000 20 2 2 40 20 23 12 无需使用eval 因为输入可以来自任何用户 我需要实现的只是整数的加法和减法 是否有任何已经存在的代码片段 或者
  • 如何让php页面从html页面接收ajax post

    我有一个非常简单的表单 其中有一个名字输入字段 我捕获了表单数据 并使用标准 jQuery 发布方法通过 ajax 将其传输到 PHP 页面 但是 我根本无法从 PHP 页面获得任何在服务器端捕获数据的响应 我不确定我做错了什么或缺少什么
  • 访问 PHP 数组对象受保护的属性

    我正在尝试在 Symfony2 中上传多个文件 我正在尝试访问以下请求对象 但无法获取参数属性 如何将文件一一上传 我得到的错误 致命错误 无法访问第 66 行 var www File src Webmuch FileBundle Ent
  • 单元测试和静态方法

    阅读并学习单元测试 试图理解以下帖子 http misko hevery com 2008 12 15 static methods are death to testability 这解释了静态函数调用的困难 我不太清楚这个问题 我一直认
  • 将 Hbase 与 PHP 集成 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经安装了 Hbase 现在我正在寻找一些 PHP 库来将 hbase 与 PHP 集成 我尝试了 2 个库 第一个是我尝试与 th

随机推荐