13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Python】Excel上でvimdiffみたいに差分を視覚化する

Last updated at Posted at 2021-06-02

#はじめに
以前pythonとマクロを組み合わせてExcel上でvimdiffみたいに差分を視覚化する方法を載せた記事を書いたのですが、pythonのみで実現する方法がわかったので此方の記事に載せておきます.
マクロを使っていたのは完全に妥協だったので過去記事削除しても良いのですが、初めてLGTM貰ってウキウキした思い入れのある記事なので消したくないです().ストックしてくれている方もいますしね!

#概要
ここでいう差分の視覚化とは以下の画像のように異列同行の2つの文字列を比較したときに片方にのみ存在する文字列を赤太字で強調表示することを指しています.
diffのサンプル.png

#開発環境
Windows10
Python-3.9.4
使用ライブラリ: difflib, pywin32

#コード

python
import difflib
import win32com.client

#カラー指定用変数
xlColorIndexAutomatic = -4105
red = -16776961

#開いているExcelbookを取得
xl = win32com.client.GetObject(Class="Excel.Application")

#ActiveSheetを取得
ws = xl.ActiveSheet

#excelは起動しているまま
xl.Visible = True

#比較したい列
column1 = "B"
column2 = "C"
#比較結果を書き込む列
column1_result ="E"
column2_result ="F"
#比較したい行範囲を書き込む.
row_s = 3
row_e = 200

#行ループ
for row in range(row_s,row_e+1):
  # cell1, cell2 : 比較対象のセル
  # cell1_result, cell2_result : 比較結果を元に差分の視覚化を行うセル
  row = str(row)
  cell1        = ws.Range(column1+row)
  cell2        = ws.Range(column2+row)
  cell1_result = ws.Range(column1_result+row)
  cell2_result = ws.Range(column2_result+row)
  # word1 : cell2の文字列との差分を記録
  # word2 : cell1の文字列との差分を記録
  word1 = ""
  word2 = ""

  if cell1.value is None and cell2.value is None:
    pass
  #片方のみが値を持つ場合は丸ごと差分として処理.
  elif cell1.value is None:
    cell2_result.value = cell2.value
    cell2_result.Font.Color = red
    cell2_result.Font.Bold = True
  elif cell2.value is None:
    cell1_result.value = cell1.value
    cell1_result.Font.Color = red
    cell1_result.Font.Bold = True
  #それぞれ値を持ち差分がある場合にdifflib.ndiffを用いて差分を取る.
  elif cell1.value != cell2.value :
    ndiff  = difflib.ndiff(str(cell1.value),str(cell2.value))

    # 差分を取った文字列(ndiff)をExcel上で視覚化しやすいように変換
    # difflib.ndiffでは
    # ・両者に存在する文字には先頭に空白" "を付加
    # ・cell1.valueにのみ存在する文字には "- "を付加
    # ・cell2.valueにのみ存在する文字には "+ "を付加
    # 空白部分が邪魔となるため除去する.
    # また"-"や"+"はよく扱われる文字であるため、あまり扱われることのない"$"に置き換える.
    # 後々この"$"の位置を探し、その後ろの文字を赤太字化することで差分を視覚化する.
    for i in ndiff:
      if i.find(" ") == 0:
        i = i[2:]
        word1 = word1 + i
        word2 = word2 + i
      elif i.find("- \n") == 0:
        i = i.replace("- \n","\n")
        word1 = word1 + i
      elif i.find("- ") == 0:
        i = i.removeprefix("- ")
        i = "$" + i
        word1 = word1 + i
      elif i.find("+ \n") == 0:
        i = i.replace("+ \n","\n")
        word2 = word2 + i
      elif i.find("+ ") == 0:
        i = i.removeprefix("+ ")
        i = "$" + i
        word2 = word2 + i
      else:
        word1 = word1 + i
        word2 = word2 + i

##### ここまででword1,word2にはそれぞれcell1,cell2のみに存在する文字の前に"$"が付加された状態.
##### ここから視覚化用の処理に入る.

    # 差分視覚化用セルに元の文字列を予め書き込んでおく.
    # また、セルの元々のフォント情報が赤字や太字であった場合に上手く視覚化出来なくなるためデフォルトに戻しておく.
    cell1_result.value           = cell1.value
    cell2_result.value           = cell2.value
    cell1_result.Font.ColorIndex = xlColorIndexAutomatic
    cell2_result.Font.ColorIndex = xlColorIndexAutomatic
    cell1_result.Font.Bold       = False
    cell2_result.Font.Bold       = False

##### cell1_resultの視覚化処理
    # k:word1のみに存在する文字の前に付加されている"$"の数を取得.
    k = len(word1) - len(word1.replace("$",""))
    # l:"$"が何文字目にあるか記録しておくためのリスト.(正確には"$"の後ろの文字)
    l = []
    for i in range(0,k):
      l.append(word1.find("$")+1)
      # 位置を記録した先頭の"$"は削除し次の"$"を探す.
      word1 = word1.replace("$","",1)
    
    # 以下やや読みづらいが、差分文字が連続しているときにまとめて赤太字化出来るようにしている.
    # 1文字ずつの処理だと時間がかかることと、多くの場合差分文字は連なっているため、こうすることで処理速度が向上する.
    i = 0
    while i != k:
      count = 1
      for n in range(1,k-i):
        if i != k-1 and l[i]+n == l[i+n]:
          count += 1
        else:
          break
      cell1_result.GetCharacters(l[i],count).Font.Color = red
      cell1_result.GetCharacters(l[i],count).Font.Bold  = True
      i += count

##### cell2_resultの視覚化処理. 詳細はcell1_resultのときと同様.
    k = len(word2) - len(word2.replace("$",""))
    l = []
    for i in range(0,k):
      l.append(word2.find("$")+1)
      word2 = word2.replace("$","",1)
    i = 0
    while i != k:
      count = 1
      for n in range(1,k-i):
        if i != k-1 and l[i]+n == l[i+n]:
          count += 1
        else:
          break
      cell2_result.GetCharacters(l[i],count).Font.Color = red
      cell2_result.GetCharacters(l[i],count).Font.Bold  = True
      i += count

#過去記事
[【Python】&【マクロ】Excel上でvimdiffみたいに差分を視覚化する][2]
[2]:https://qiita.com/dbc_spr/items/e261e18d96b8fd2d4a83

#Special Thanks
以下の質問記事に@Carteletさんから回答を頂きpythonのみで実現することが出来ました。@Carteletさん誠にありがとうございました。
[【Python】pywin32でExcelのセルの一部の文字列に対してフォントの変更を行いたい][1]
[1]:https://qiita.com/dbc_spr/questions/655bf1fc50445773d810

以上です. どなたかの役に立てばうれしい限りです.

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?