python diff函数_使用Python创建你自己的diff工具
為什么我需要自己的diff工具?
我經常使用git跟蹤我的編碼項目、文章、業務工作等等。git的一個美妙之處在于,你可以通過簡單地使用其內置的diff功能來輕松地比較你的工作的不同狀態。要使用這個功能,你只需要滿足兩個約束:首先,你需要一個git存儲庫,其次,該文件需要由git存儲庫進行跟蹤。
但是,如果您只想修改單個文件,并將其與舊版本進行比較,所有這些操作都不需要用到git存儲庫,那該怎么辦呢?這就是本文的意義所在。本文的目標是創建一個diff工具,它允許你比較一個文件的兩個版本:
不需要git存儲庫和
構建在Python標準庫之上!
此外,我們的diff工具應該能夠將計算的差異導出到一個HTML文件中。要閱讀這篇文章,你只需要會Python。本文是專門為Python 3.8.2 (CPython)編寫的。你可以在GitHub上找到源代碼。就介紹到這里,讓我們來看看它吧!
unified_diff() 函數
Python標準庫包含一個名為difflib的模塊。根據文檔,這個模塊提供了用于比較序列的類和函數。此外,還有各種可用的輸出格式[1]。
在檢查該模塊時,unified_diff()函數從所有其他函數中脫穎而出。通過查看文檔提供的示例和生成的輸出,我發現它與我們正在尋找的差異計算函數非常相似。它需要多達8個參數,但只有兩個是必需的:
a:字符串列表(必需)
b:字符串列表(必需)
fromfile:用于顯示第一個文件的名稱(默認值:'')
tofile:用于顯示第二個文件的名稱(默認值:'' )
fromfiledate:第一個文件的修改時間(默認值: '')
tofiledate:第二個文件的修改時間(默認值: '')
n:上下文行數*(默認值:3)
lineterm:添加在控制行(帶有—、+++或@@的行)末尾的字符,以便io.IOBase.readlines()和io.IOBase.writelines()能被正確處理(默認值:'\n')。
*上下文行用于向用戶在發生更改的地方提供上下文
實際上,unified_diff()函數會接受兩個字符串列表并對它們進行比較。如果它們是相等的,delta就為空。如果存在任何差異,則返回各自的delta。舉個簡單的例子:你打算和你最好的朋友一起舉辦一個披薩派對,然后寫下了你需要先買的配料。為簡單起見,此購物清單是一個簡單的文本文件(my_shopping_list.txt),內容如下所示:
你把它發送給你的朋友,他加了一種配料,因為你們家里都沒有了:salami(意大利臘腸)。此外,他還識別出了你的打字錯誤并進行了糾正。為了能夠正確地將這些更改傳遞給我們的diff工具,他復制了該清單并將其重命名為friends_shopping_list.txt。以下是最終的購物清單:
當然,這是一個相當簡單的例子,文本不是很長,所以能很容易地被人為處理。但是,讓我們增加點樂趣,我們繼續看這個例子。為了計算這兩個文件之間的差異,我們將它們讀入內存,并將它們傳遞給unified_diff()函數:
首先,我們導入了difflib和sys。其次,我們讀取這兩個文件的內容,并將它們保存到單獨的變量(file1和file2)中。因為我們需要字符串列表,所以我們使用了readlines()。隨后,我們計算了兩個列表的delta,并通過sys.stdout.writelines()將其寫到stdout。
執行腳本后生成的結果如下:
從輸出來看,單詞cheese沒有被修改,而是被用作上下文行,因為下面的行被修改了。tomates被移除了,tomatoes和salami被加了進去。如果您查看打印出的delta的頭部,你可以看到,我們并沒有得到有關--- 和 +++代表什么,或者它們代表什么文件的任何信息。讓我們通過將文件名添加到unified_diff()函數來調整一下腳本:
現在,運行該腳本生成結果如下:
太棒了!我們實現了一個簡單的腳本,它可以計算并打印兩個文件內容之間的差異。讓我們繼續并將其轉換為一個命令行工具。
構建一個命令行工具
為了將我們的腳本轉換成一個有用的命令行工具,我們使用了Python的argparse模塊。首先,我們將之前編寫的代碼放入一個名為create_diff()的函數中,該函數接受兩個參數,old_file和new_file。它們都是Path對象[2]。我們使用傳遞的Path對象來讀取它們的內容,并使用該Path對象的name屬性來獲取所提供的文件的名稱。由于我們的小腳本現在是要更加通用,不再局限于購物清單,所以我們將我們的代碼放入一個名為diff_tool.py的新文件中(這個名稱更適合我們的腳本)。到目前為止,該腳本是這樣的:
接著,我們定義一個新函數main(),它負責總的工作流:
首先,我們定義了一個新的參數解析器。我們告訴該解析器接受兩個參數,old_file_version和new_file_version。兩者都是必需的。調用parse_args()將解析命令行輸入并將輸入轉換為正確的格式。隨后,兩個命令行參數都會被訪問并且被轉換為Path對象。然后,我們使用old_file和new_file作為參數調用create_diff()。
備注:如果您想要了解更多關于argparse模塊的內容,我強烈推薦Python的argparse教程[3],它提供了更詳細的Python命令行解析介紹。
現在,如果我們不帶任何參數執行該腳本,它會告訴我們,哪些參數是必需的:
提供兩個購物清單后仍然會生成預期的輸出:
到目前為止,我們通過將你的簡短腳本從頭開始轉換成一個簡單的命令行工具來構建了一個簡單的diff工具—很酷!現在,我們將添加更多的行來支持HTML輸出。
以HMTL格式提供差異
difflib模塊提供了一個HtmlDiff類,它可以被用來創建一個HTML表(或一個包含表的完整HTML文件),該表會通過將行間和行內變為高亮顯示來顯示文本的并排、逐行比較。在我們的示例中,我們使用了HtmlDiff.make_file()函數,它返回了一個表示完整HTML文件的字符串。后者逐行高亮顯示了任何差異。
因此,我們將我們的腳本擴展如下:
create_diff()函數現在接受一個額外的第三個參數output_file,它也是一個Path對象。我們會將我們的HTML差異寫入這個文件。我們檢查是否傳遞了output_file。如果是,我們以HTML格式計算差異并將其保存到這個傳遞的文件中。
備注:我們使用w模式進行寫操作。如果該文件已經存在,則會被提前清空。
如果output_file沒有被傳遞,我們會計算標準差異并將其寫到stdout。
我們通過注冊一個附加的可選命令行參數--html使其以一個文件名作為輸入來擴展了main()函數。如果提供了文件名,則將其轉換為Path對象并傳遞給create_diff()。
執行以下命令之后,您的當前工作目錄中就有了一個diff.html文件,你可以使用你最喜歡的瀏覽器來打開該文件去查看實際的差異。
總結
恭喜,你已經通過本文創建了它!在閱讀本文時,你了解了如何使用Python的difflib模塊計算一個簡單的diff。此外,你還能夠使用Python的argparse模塊將您的短小diff腳本轉換成一個命令行工具。隨后,你添加了幾行代碼來支持以HTML作為輸出格式。
接下來是什么?你可以查看difflib文檔[1],了解計算diff的各種方法,搜索其他類型的diff,并進一步擴展你的diff-tool。另外,你可以查看本文的GitHub存儲庫并計算file.md和file_update.md之間的差異。你找到所有的變化了嗎?
希望你喜歡閱讀這篇文章。一定要與你的朋友和同事進行分享哦!如果你還沒有,你可以考慮關注我的推特@DahlitzF。保持好奇心,持續編碼!
參考資料
difflib文檔
Path文檔
argparse文檔
內置open()函數
總結
以上是生活随笔為你收集整理的python diff函数_使用Python创建你自己的diff工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: k8s 查看mysql 日志_k8s 使
- 下一篇: python字符编码在哪里_快速入手Py