一個小專案長達十二年的故事(第一次關於 BIRMA.NET,坦白說是第一手資料)

這個計畫的誕生可以認為是我在 2007 年底的某個時候想到的一個小想法,它注定要在 12 年後才能找到最終的形式(當然,在這個時間點 - 儘管目前的實施,根據對作者來說,非常滿意)。

這一切都始於,在履行我當時在圖書館的官方職責的過程中,我提請注意這樣一個事實:將書籍(和音樂)出版物目錄的掃描文本中的數據輸入現有數據庫的過程,顯然,可以顯著簡化和自動化,利用輸入所需的所有資料的有序性和可重複性,例如文章作者的姓名(如果我們正在談論文章的集合)、文章的標題文章(或目錄中反映的副標題)和目前目錄項目的頁碼。 起初,我幾乎相信可以在網路上輕鬆找到適合執行此任務的系統。 當我因找不到這樣的專案而感到驚訝時,我決定嘗試自己實現它。

經過相當短的時間後,第一個原型開始工作,我立即開始在日常活動中使用它,同時在我手上的所有示例上對其進行調試。 幸運的是,在我平時的工作場所,我絕不是一名程式設計師,但我仍然在工作中經歷了明顯的“停機時間”,在此期間我正在集中調試我的想法——這在當前的現實中幾乎是不可想像的事情,這意味著每日報告白天完成的工作。 完善程序的過程總共花費了不少於一年的時間,但即使在此之後,結果也很難被稱為完全成功——最初制定了太多不同的概念,但實施起來並不完全清楚:可選的元素可以被跳過; 向前查看元素(為了將先前的元素替換到搜尋結果中); 甚至我們自己嘗試實現正規表示式(具有獨特的語法)之類的東西。 我必須說,在此之前,我已經放棄了一定程度的程式設計(大約 8 年,甚至更多),因此將我的技能應用於有趣且必要的任務的新機會完全吸引了我的注意力。 毫不奇怪,最終的源代碼——在我沒有任何明確的設計方法的情況下——很快就變成了難以想像的大雜燴,由C 語言中的不同部分與C++ 的一些元素和可視化編程的各個方面(最初是它)組成。決定使用 Borland C++ Builder 這樣的設計系統 - “幾乎是 Delphi,但是是 C 語言”)。 然而,所有這一切最終在我們圖書館的日常活動自動化方面取得了成果。

同時,為了以防萬一,我決定參加培訓專業軟體開發人員的課程。 我不知道那裡是否真的有可能從頭開始學習“成為一名程式設計師”,但考慮到我當時已經擁有的技能,我能夠在某種程度上掌握當時更相關的技術,例如如C#、. NET下開發的Visual Studio,以及Java、HTML和SQL相關的一些技術。 整個培訓總共花了兩年時間,並作為我的另一個專案的起點,該專案最終持續了幾年——但這是一個單獨出版物的主題。 在這裡,需要注意的是,我嘗試調整我在所描述的專案中已經進行的開發,以在 C# 和 WinForms 中創建一個成熟的視窗應用程序,實現必要的功能,並將其用作即將推出的文憑項目。
隨著時間的推移,這個想法開始在我看來值得在諸如“LIBKOM”和“CRIMEA”等各圖書館代表參加的年度會議上表達。 這個想法是的,但我當時沒有實施。 然後我也希望有人能用更有效的方法來重寫它。 不管怎樣,到2013年,我決定寫一份我的前期工作報告,並發送給會議組委會,併申請參加會議的資助。 令我有些驚訝的是,我的申請獲得了批准,我開始對專案進行一些改進,準備在會議上進行演示。

到那時,該專案已經獲得了一個新名稱 BIRMA,獲得了各種額外的(與其說是完全實現,不如說是假設的)功能 - 所有詳細資訊都可以在我的報告中找到.

說實話,很難說 BIRMA 2013 是一個完整的活動。 坦白說,這是一件非常草率的匆忙製作的工藝品。 在程式碼方面,幾乎沒有什麼特別的創新,除了為解析器創建某種統一語法的相當無助的嘗試,其外觀讓人想起 IRBIS 64 格式化語言(事實上,還有 ISIS 系統 -用括號作為循環結構;為什麼當時我覺得它看起來很酷)。 解析器無可救藥地偶然發現了這些適當類型的圓括號(因為圓括號還發揮了另一個作用,即它們在解析過程中標記了可以跳過的可選結構)。 我再次建議所有想要更詳細地了解當時難以想像、不合理的 BIRMA 語法的人,請參閱我當時的報告。

總的來說,除了與我自己的解析器作鬥爭之外,關於這個版本的程式碼我沒有什麼可說的——除了將現有原始碼反向轉換為C++,同時保留.NET 程式碼的一些典型特徵(說實話,它是很難理解,到底是什麼促使我把所有東西都移回去——可能是出於對源代碼保密的愚蠢恐懼,就好像它相當於可口可樂的秘密配方一樣)。

也許這個愚蠢的決定也是難以將生成的 DLL 庫與自製工作站的現有接口配對以將數據輸入電子目錄的原因(是的,我沒有提到另一個重要事實:從現在開始,所有BIRMA“引擎”的程式碼與預期一致,它與介面部分分離並封裝在適當的DLL中)。 為什麼有必要為此目的編寫一個單獨的工作站,無論如何,在其外觀和與用戶交互的方法上,無恥地複製了 IRBIS 64 系統的同一工作站“目錄生成器” - 這是一個單獨的問題。 簡而言之:它為我當時的畢業專案開發提供了必要的堅實性(否則僅難以消化的解析器引擎在某種程度上是不夠的)。 此外,我在使用自己的模組(用 C++ 和 C# 實作)實作 Cataloger 工作站的介面以及直接存取我的引擎時遇到了一些困難。

總的來說,奇怪的是,未來 BIRMA.NET 的這個相當笨拙的原型注定會成為我未來四年的「主力」。 不能說,在這段時間裡,我至少沒有嘗試尋找新的、更完整地實施長期想法的方法。 在其他創新中,應該已經存在可以包含可選元素的嵌套循環序列 - 這就是我將如何實現出版物書目描述和各種其他有趣事物的通用模板的想法。 但在我當時的實際活動中,這一切的需求並不大,當時的實現對於輸入目錄來說已經足夠了。 另外,我們圖書館的發展向量開始越來越偏離博物館檔案的數位化、報道等我不感興趣的活動,最終迫使我最終離開它,讓位給那些願意做的人。對這一切感到更加滿意。

矛盾的是,正是在這些戲劇性事件之後,當時已經具備典型長期建設項目所有特徵的 BIRMA 項目似乎開始呈現出期待已久的新生命! 我有更多的空閒時間進行閒思,我再次開始梳理萬維網以尋找類似的東西(幸運的是,現在我已經可以猜測尋找所有這些不僅在任何地方,而且在 GitHub 上),並且在某個地方今年年初,我終於在不起眼的名字下看到了知名Salesforce公司的相應產品 戈爾普。 就其本身而言,它幾乎可以完成我從這樣的解析器引擎中需要的一切- 即,智能地將單個片段與任意但結構清晰的文本隔離,同時為最終用戶提供相當用戶友好的界面,包括可理解的本質,例如模式、模板和出現,同時使用熟悉的正則表達式語法,由於劃分為指定的語義組進行解析,因此可讀性變得無與倫比。

總的來說,我決定這就是 戈爾普 (我想知道這個名字是什麼意思?也許是某種「通用的正則解析器」?)——這正是我長期以來一直在尋找的。 確實,它針對我自己的需求的立即實現存在這樣一個問題:該引擎需要過於嚴格地遵守源文本的結構順序。 對於某些報告,例如日誌檔案(即,開發人員將它們放置為使用該專案的明確範例),這非常合適,但對於掃描目錄的相同文本,則不太可能。 畢竟,帶有目錄的同一頁面可以以「目錄」、「目錄」以及我們不需要放置在預期分析結果中的任何其他初步描述(並手動將其刪除)開頭。每次也很不方便)。 此外,在各個重複元素(例如作者姓名、標題和頁碼)之間,頁面可能包含一定量的垃圾(例如圖畫和隨機字元),如果能夠隔間。 然而,最後一個方面還沒有那麼重要,但由於第一個方面,現有的實現無法從某個地方開始尋找文本中必要的結構,而是簡單地從頭開始處理,沒有找到在那裡指定了模式並……結束了我的工作。 顯然,需要進行一些調整,至少在重複結構之間留出一些空間,這讓我重新開始工作。

另一個問題是,該專案本身是用Java 實現的,如果我計劃將來實現某種方法,將該技術與熟悉的應用程式連接起來,以便將資料輸入現有資料庫(例如Irbis 的「Cataloger」),那麼至少至少在 C# 和 .NET 中執行此操作。 這並不是說 Java 本身是一種糟糕的語言——我甚至曾經用它來實現一個有趣的窗口應用程序,該應用程序實現了家用可編程計算器的功能(作為課程項目的一部分)。 而且在文法方面它與同樣的升C 非常相似。 嗯,這只是一個優點:我比較容易完成現有的專案。 然而,我不想再次陷入這個相當不尋常的窗口(或者更確切地說,桌面)Java 技術的世界 - 畢竟,該語言本身並不是為這種用途“量身定制”的,而且我根本不渴望重複以前的經驗。 也許正是因為 C# 與 WinForms 結合起來更接近我們許多人曾經開始使用的 Delphi。 幸運的是,很快就找到了必要的解決方案 - 以專案的形式 IKVM網絡,這使得將現有 Java 程式轉換為託管 .NET 程式碼變得容易。 確實,當時該專案本身已經被作者放棄,但它的最新實現使我能夠相當成功地對源文本執行必要的操作 戈爾普.

因此,我進行了所有必要的更改,並將其全部組裝到適當類型的 DLL 中,該 DLL 可以輕鬆地被 Visual Studio 中建立的 .NET Framework 的任何專案「拾取」。 同時,我創建了另一個層以方便呈現返回的結果 戈爾普,以相應的資料結構的形式,以便在表格視圖中處理(以行和列為基礎;字典鍵和數字索引)。 嗯,處理和顯示結果所需的實用程式本身編寫得相當快。

此外,為新引擎調整模板以教其解析目錄掃描文字的現有樣本的過程並沒有引起任何特殊的複雜性。 事實上,我甚至根本不需要參考先前的模板:我只是從頭開始創建所有必要的模板。 此外,如果設計用於與系統的先前版本一起使用的模板為可以在其幫助下正確解析的文本設置了相當狹窄的框架,那麼新引擎已經可以開發適合多種類型標記的相當通用的模板一次。 我甚至嘗試為任意目錄文本編寫某種綜合模板,當然,即使為我提供了所有新的可能性,特別是實現相同嵌套重複序列的能力有限(例如,連續幾個作者的姓氏和首字母),結果證明這是烏托邦。

也許將來可以實現元模板的某種概念,它能夠立即檢查來源文字是否符合多個可用模板,然後根據所獲得的結果選擇最合適的一個,使用某種智慧演算法。 但現在我更關心另一個問題。 像這樣的解析器 戈爾普儘管它具有多種功能並且我做了很多修改,但它本質上仍然無法完成我自己編寫的解析器從第一個版本就能夠完成的看似簡單的事情。 即:他能夠從來源文字中尋找並提取與正確位置使用的範本中指定的遮罩相符的所有片段,而對給定文字在這些片段之間的空格中包含的內容完全不感興趣。 到目前為止,我只稍微改進了新引擎,使其能夠從當前位置搜尋給定序列的此類掩碼的所有可能的新重複,從而使文本中存在完全不存在的任意字元集的可能性。在解析中未被考慮,包含在偵測到的重複結構之間。 然而,無論使用相應遮罩搜尋前一個片段的結果如何,這都無法設定下一個遮罩:所描述的文字結構的嚴格性仍然沒有為任意包含不規則字元留下空間。

如果對於我遇到的目錄範例,這個問題似乎還沒有那麼嚴重,那麼當嘗試將新的解析機制應用於解析網站內容的類似任務(即相同的解析)時,其局限性就在這裡,它們顯而易見。 畢竟,為 Web 標記片段設定必要的遮罩非常容易,我們正在尋找的資料(需要提取)應該位於這些遮罩之間,但是我們如何強制解析器立即轉到下一個類似的片段,儘管所有可能的標籤和HTML 屬性都可以放置在它們之間的空格中?

經過一番思考,我決定引入幾種服務模式 (%all_之前) и (%all_after),其明顯目的是確保在其後面的任何模式(遮罩)之前跳過來源文字中可能包含的所有內容。 此外,如果 (%all_之前) 簡單地忽略所有這些任意包含物,然後 (%all_after)相反,允許它們從前一個片段移動後添加到所需的片段。 聽起來很簡單,但是為了實現這個概念,我必須再次梳理 gorp 原始程式碼以進行必要的修改,以免破壞已經實現的邏輯。 最後,我們成功地做到了這一點(儘管我的解析器的實現甚至是非常非常第一個,儘管有很多錯誤,但已經編寫完成,甚至更快 - 在幾週內)。 從現在開始,該系統呈現出真正通用的形式——距首次嘗試使其發揮作用至少 12 年。

當然,這並不是我們夢想的結束。 您也可以使用任何可用的函式庫來實作自由語法,用 C# 完全重寫 gorp 範本解析器。 我認為應該顯著簡化程式碼,這將使我們能夠擺脫現有 Java 原始碼形式的遺留問題。 但是使用現有類型的引擎,也很有可能做各種有趣的事情,包括嘗試實現我已經提到的元模板,更不用說解析來自各個網站的各種數據(但是,我不排除現有的專用軟體工具更適合於此——我只是還沒有使用它們的適當經驗)。

順便說一句,今年夏天我已經收到了一封來自使用 Salesforce 技術的公司(原始開發者)的電子郵件邀請 戈爾普),透過後續在裡加工作的面試。 不幸的是,目前我還沒有準備好進行此類重新部署。

如果此材料引起了一些興趣,那麼在第二部分中,我將嘗試使用 Salesforce 中使用的實作範例更詳細地描述編譯和隨後解析模板的技術 戈爾普 (我自己的添加,除了已經描述的幾個功能詞之外,幾乎沒有對模板語法本身進行任何更改,因此原始系統的幾乎所有文檔 戈爾普 也適合我的版本)。

來源: www.habr.com

添加評論