小記:爲開源項目增長一個新功能的開發歷程

本篇文章不是爲了記開發流水帳,而是想把開發過程的遇到的問題以及解決思路和你們進行交流和學習。我是一名普普統統的 PHP 工程師,但願對初級開發同窗有所幫助。具體的心得體會見文末的總結php

本月初,我在 GitHub 上開源了一個本身的小項目:chinese-typesetting。這是一個糾正中文文案排版的 Composer 包。html

chinese-typesetting 包含如下功能:git

  • 在中文與英文字母/用於數學、科學和工程的希臘字母/數字之間添加空格;
  • 有限度的全角轉半角(英文、數字、空格以及一些特殊字符等使用半角字符);
  • 修復錯誤的標點符號;
  • 清除 HTML 標籤的樣式;
  • 清除空的 HTML 標籤;
  • 清除段首縮進;

本週,公司開發事務很少,無加班,因而開始構思新功能糾正英語專有名詞大小寫的實現。github

英語專有名詞的數據來源

首先,面臨的第一個問題是:web

英語專有名詞的數據從哪來?chrome

我最早想到的是 Python 有一個天然語言處理的包 NLTK,這個包有個名爲 pos_tag 的函數,能夠用來識別並標註每一個單詞的詞性,其中被標註爲 NNP 或 NNPS 的單詞就是專有名詞(Proper Noun)。我猜測,NLTK 數據包裏應該有一個對應的專有名詞數據集,可是,苦於能力有限,我一直沒有找到。編程

上述的路徑走不通後,我又經過 Google 搜索,發現經過網絡字典來獲取數據是一條可行的方案。經過這一方法,終於在 Wiktionary 找到了英語專有名詞列表。因而,利用 Python 寫了一個爬蟲小腳本,爬取了對應的數據。瀏覽器

最後,就是對爬取到的數據進行了一些整理和篩選。網絡

篩選方案以下:dom

  • 使用 is_numeric() 方法,剔除諸如 007 等詞彙;
  • 使用 '/\W/' 正則,剔除諸如 ǃXóõ 等詞彙;
  • 剔除 strlen 方法,剔除 A 等單字節詞彙;
  • 剔除跟 HTML、CSS、JavaScript 保留字衝突的詞彙;

如何讓使用者定製專有名詞數據

最初的代碼以下:

/**
 * 專有名詞使用正確的大小寫
 * Correct English proper nouns.
 *
 * @param $text
 *
 * @return null|string|string[]
 */
public function properNoun($text)
{
    $dict = include __DIR__ . '/../data/dict.php';
    foreach ($dict as $noun) {
        $text = preg_replace("/\b{$noun}\b/i", $noun, $text);
    }
    return $text;
}

以後想到,若是使用這個方法的開發者想擴展或者忽略某些專有名詞,那該怎麼辦呢?
因而,我又將 properNoun() 方法改造以下:

/**
 * 專有名詞使用正確的大小寫
 * Correct English proper nouns.
 *
 * @param $text
 * @param array $extend
 * @param array $ignore
 *
 * @return null|string|string[]
 */
public function properNoun($text, array $extend = [], array $ignore = [])
{
    $dict = include __DIR__ . '/../data/dict.php';
    if ($extend) {
        $dict = array_merge($dict, $extend);
    }
    if ($ignore) {
        $dict = array_diff($dict, $ignore);
    }

    foreach ($dict as $noun) {
        $text = preg_replace("/\b{$noun}\b/i", $noun, $text);
    }
    return $text;
}

如何改進和優化代碼邏輯

我在寫這個功能的時候,也在研究和參考一些現有開源項目的實現邏輯。在看到開源項目 auto-correct 的一個 commit 上後(PS:這個 PR 是社區大神 overtrue 提交的。),我又將 properNoun() 方法改造以下:

public function properNoun($text, array $extend = [], array $ignore = [])
{
    $dict = include __DIR__ . '/../data/dict.php';
    if ($extend) {
        $dict = array_merge($dict, $extend);
    }
    if ($ignore) {
        $dict = array_diff($dict, $ignore);
    }
    foreach ($dict as $noun) {
        $text = preg_replace("/(?<!\.|[a-z]){$noun}(?!\.|[a-z])/i", $noun, $text);
    }
    return $text;
}

如何避免過分替換

在我覺得就要大功告成的時候,我用以前寫好的 PHPUnit 單元測試代碼進行了測試,結果報出了錯誤,在上述方法中,若是傳入的參數是包含 HTML 標籤的富文本,那麼 HTML 的元素、元素屬性以及值都有可能會被替換。

如何避免過分替換這個問題呢?也就是說:

只替換文本,而忽略 HTML 標籤及標籤內部的內容?

我嘗試寫了好幾套匹配方案,都失敗了。最後仍是請出了 Google 大神來幫忙。這裏,搜索的關鍵字很重要,最好想把你要搜索的關鍵詞翻譯成對應的英文單詞,這樣搜索出的結果會令你更滿意。結果我找到了解決方案:Matching A Word / Characters Outside Of Html Tags

經過上面這部文章的提示,我又將 properNoun() 方法改造以下:

public function properNoun($text, array $extend = [], array $ignore = [])
{
    $dict = include __DIR__ . '/../data/dict.php';
    if ($extend) {
        $dict = array_merge($dict, $extend);
    }
    if ($ignore) {
        $dict = array_diff($dict, $ignore);
    }
    foreach ($dict as $noun) {
        // Matching proper nouns Outside Of Html Tags
        $text = preg_replace("/(?<!\.|[a-z]){$noun}(?!\.|[a-z])(?!([^<]+)?>)/i", $noun, $text);
    }
    return $text;
}

開發總結

  • 學會***;
  • 善用 Google、Github 和 StackOverflow,這三樣「神器」會幫你解決掉開發過程當中遇到的絕大部分(或者說全部)問題;
  • 學會一些 Google 搜索小技巧。例如將搜索關鍵字翻譯成英語單詞,這樣的搜索結果會令你更滿意;
  • 英語真的很重要。最起碼你應該在 Chrome 瀏覽器上安裝一個 Google 翻譯 的插件;
  • PHPUnit 真的頗有用,特別是在頻繁增改功能或者須要代碼重構的項目中。
  • 不要讓本身僅限於一個編程語言,學習另一門或多門語言做爲輔助,有益於拓展思路和開拓眼界。
  • 多逛逛 Laravel China 這樣的高品質社區;

最後的話

若是還有什麼須要說的話,那就是求 Star 啦,哈哈哈哈哈。項目地址:https://github.com/jxlwqq/chinese-typesetting

相關文章
相關標籤/搜索