# find the different files between 2 folders | LittleVoice-g-string

LittleVoice-g-string

苦しむために生きないであなた自身を愛してくれ

find the different files between 2 folders


1.課題

  • 一つの圧縮ファイルを解凍して、2つのフォルダを得た。
    • A_fullフォルダ:エラーなく解凍でき、ファイル紛失のないフォルダ
    • B_errorフォルダ:途中エラーが出てしまい、一部ファイルが紛失してしまったフォルダ
  • この2つのフォルダにそれぞれ8つのサブフォルダがあり、サブフォルダ名は以下の対応関係である。
    • A_full:20210201 ~ B_error:a1
    • A_full:20210202 ~ B_error:a10
    • A_full:20210203 ~ B_error:a11
    • A_full:20210204 ~ B_error:a2
    • A_full:20210205 ~ B_error:a
    • A_full:20210206 ~ B_error:A1
    • A_full:20210207 ~ B_error:A19
    • A_full:20210208 ~ B_error:S
      A_full B_error
  • フォルダ内のファイル名はそれぞれ一致している。
    • 20210201
    • a1
  • 探しだしたファイルをフォルダごとに格納する、以下の通りになってほしい。
    フォルダ構造 20210201-a1後の結果

2.A_fullとB_error内のファイル名をマッチする

まずA_fullとB_errorのパスを用意する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kk_full=[k for k,w,t in os.walk('./A_full')]
ww_full=[w for k,w,t in os.walk('./A_full')]
tt_full=[t for k,w,t in os.walk('./A_full')]

kk_error=[k for k,w,t in os.walk('./B_error')]
ww_error=[w for k,w,t in os.walk('./B_error')]
tt_error=[t for k,w,t in os.walk('./B_error')]

kk_full
#['./A_full',
'./A_full\\20210201',
'./A_full\\20210202',
'./A_full\\20210203',
'./A_full\\20210204',
'./A_full\\20210205',
'./A_full\\20210206',
'./A_full\\20210207',
'./A_full\\20210208']

file_name_full = [kk_full[i].split('/')[-1].split('\\')[-1] for i in range(1,len(kk_full))]
file_name_error = [kk_error[i].split('/')[-1].split('\\')[-1] for i in range(1,len(kk_error))]

file_name_error
#['a', 'A1', 'a1', 'a10', 'a11', 'A19', 'a2', 'S']

並び替えルールを定義する(@ながたさんに感謝!)
考え方としては、A,a,SをすべてUnicode値に変換することによって、順位を人為的に付けさせる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def rank_filename(value):
if len(value) <= 1:
for i in count():
try:
_ = chr(i)
except ValueError:
max_char_number = i-1
break
left,right = value.lower(),chr(max_char_number)
else:
left,right = value[0],''.join(list(value)[1:])
#A-Zのほうがa-zより小さいので、それを入れ替える
left_after = ''
for c in left:
if 'a' <= c <= 'z':
left_after += chr(ord('A')+(ord(c)-(ord('a')))+5)
elif 'A' <= c <= 'Z':
left_after += chr(ord('a')+(ord(c)-(ord('A')))+5)
else:
left_after += c
left = left_after
return left,right

定義したrank_filename関数をsortのkeyに応用する。

1
2
3
file_name_sort = sorted(file_name_error,key=rank_filename)
file_name_sort
#['a1', 'a10', 'a11', 'a2', 'a', 'A1', 'A19', 'S']

参考①A,a,S,-などのUnicode値は以下の通りである。

1
2
ord('A'),ord('S'),ord('a'),ord('s'),ord('-')
#(65, 83, 97, 115, 45)

3.すべてのファイルのフルパスを取得する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
folder_path_sorted = ['./B_error/'+i for i in file_name_sort]
folder_path_full = ['./A_full/'+i for i in file_name_full]

def make_full_path(list_):
full_path = []
for i in list_:
for v,k,m in os.walk(i):
path_tmp = [os.path.join(v,x) for x in m]
full_path.append(path_tmp)
return full_path

full_path_sorted = make_full_path(folder_path_sorted)
full_path_all = make_full_path(folder_path_full)

full_path_sorted[7][0]
#'./B_error/S\\全員に1レッスンプレゼント!仙台校移転記念♪トライアルキャンペーン開催!!.eml'

念の為、BフォルダとAフォルダの対応関係を一つの辞書に格納した。

1
2
3
4
5
6
7
8
9
10
file_path_dict=dict(zip(folder_path_sorted,folder_path_full))
file_path_dict
#{'./B_error/a1': './A_full/20210201',
'./B_error/a10': './A_full/20210202',
'./B_error/a11': './A_full/20210203',
'./B_error/a2': './A_full/20210204',
'./B_error/a': './A_full/20210205',
'./B_error/A1': './A_full/20210206',
'./B_error/A19': './A_full/20210207',
'./B_error/S': './A_full/20210208'}

4-1.ファイル名をもとに比較する

一度サブフォルダの順序を変えたので、Bフォルダのパス情報を新たに格納する

1
2
3
kk_error_sorted=[k for i in folder_path_sorted for k,w,t in os.walk(i)]
# ww_sorted=[w for i in folder_path_sorted for k,w,t in os.walk(i)]
tt_error_sorted=[t for i in folder_path_sorted for k,w,t in os.walk(i)]

すでに処理済み👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
kk_full=[k for k,w,t in os.walk('./A_full')]
ww_full=[w for k,w,t in os.walk('./A_full')]
tt_full=[t for k,w,t in os.walk('./A_full')]

#フォルダごとの差分を取り出す
list_ = [[]]*8
for i in range(len(tt_error_sorted)):
list_.append(list(set(tt_full[i+1])-set(tt_error_sorted[i])))
list_
[[],
[],
[],
[],
[],
[],
[],
[],
['Nintendo Switch『モンスターハンターライズ』の最新映像が公開。 _ トピックス _ Nintendo.html',
'The savings are clear.eml',
'【Nintendo Switch】期間限定セール・新作ほか、ソフトのご紹介.eml'],
['Your question of the week from Vocabulary.com.eml',
'東京2020大会:オリンピック&パラリンピックの世界へ.eml'],
['新年のご挨拶.eml', 'もっちもっちの弾力に、脈打つ熟成香。魅惑のブランデーあんぱん.eml'],
['ソフトウェアを安全に保護するためのより良い方法.eml',
'ゴーストライター騒動から7年 新垣隆の「覚悟」.eml',
'Communication trends of 2020.eml'],
['東京2020大会:オリンピック&パラリンピックの世界を知り尽くそう.eml', '宇賀なつみ、思わず息をのむ.eml'],
['深度学习之路(一):用LSTM网络做时间序列数据预测 - 简书.html'],
['Our cool-productivity-killer newsletter.eml'],
['明智光秀、最後の夜は…….eml']]

dict_path = dict(zip(kk_full[1:],list_[8:]))

4-2.差分のファイルを新しくフォルダごとに保存する

1
2
3
4
5
6
7
8
9
10
11
12
13
save_dir='./new/'
if not os.path.isdir(save_dir):
os.makedirs(save_dir)

for i in kk_full[1:]:
# print(i.split('/')[1].split('\\')[1])
for m in dict_path[i]:
from_path = os.path.join(i,m)
# print(from_path)
to_path = os.path.join(save_dir,i.split('/')[1].split('\\')[1])
if not os.path.isdir(to_path):
os.makedirs(to_path)
copy(from_path,to_path)

5-1.内容ともとに比較する

今回のファイルにはeml,html(サンプルにはないけど、ほんとはmsgファイルも)が入っているため、以下の内容抽出するための関数を作成した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def extract_text(list_):
text = []
file_id = []
for i in list_:
if i.split('.')[-1] == 'eml':
file_id.append(i)
with open(i,'r',encoding = 'utf-8-sig') as f:
text_ = f.read()
text.append(text_)
# print('extracted',i)
elif i.split('.')[-1] == 'msg':
file_id.append(i)
subject = extract_msg.Message(i).subject
date = extract_msg.Message(i).date
body = extract_msg.Message(i).body
text_ = subject+date+body
text.append(text_)
# print('extracted',i)
elif i.split('.')[-1] == 'html':
file_id.append(i)
htmlfile = open(i, 'r', encoding='utf-8-sig')
htmlhandle = htmlfile.read()
soup = BeautifulSoup(htmlhandle, 'html.parser')
text.append(soup.text)
# print('extracted',i)

# dict_ = dict(zip(file_id,text))
return file_id,text

上記関数を使って、フォルダごとの内容を比較し、フルパスを取得する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
differ_ids=[]
for i in range(len(full_path_all)):
file_id,text_sorted =extract_text(full_path_all[i])
file_id_error,text_error =extract_text(full_path_sorted[i])
dict_ = dict(zip(text_sorted,file_id))
differ_id = []
for m in text_sorted:
if m not in text_error:
differ_id.append(dict_[m])
differ_ids.append(differ_id)

differ_ids[1]
#['./A_full/20210202\\Your question of the week from Vocabulary.com.eml',
'./A_full/20210202\\東京2020大会:オリンピック&パラリンピックの世界へ.eml']

5-2.差分を保存する

1
2
3
4
5
6
7
8
9
10
save_dir='./new'
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
for i in differ_ids:
for m in i:
# print(m.split('/')[2].split('\\')[0])
to_path = os.path.join(save_dir,m.split('/')[2].split('\\')[0])
if not os.path.isdir(to_path):
os.makedirs(to_path)
copy(m,to_path)

appendix フォルダ名を変更する

投げたフォルダ(pathで指定)内のサブフォルダ名をname_listの順序通りに名前を変更させる。

1
2
3
4
5
6
7
8
9
10
11
12
def rename(path):
i=0
name_list=['kaggle','tableau','pytorch'] #自由設定できる
FileList = os.listdir(path)
for files in FileList:
oldDirPath = os.path.join(path, files)
if os.path.isdir(oldDirPath):
rename(oldDirPath)
newDirPath=os.path.join(path,name_list[i])
os.rename(oldDirPath, newDirPath)
print(files +' has changed as '+newDirPath)
i += 1

Next

  • すでに同じ機能を果たしている無料ツールがあるらしいので、ユーザーが使いやすいため、webツールを作れるといいなと思います。
  • 今回は単にeml,msg,htmlを読み込んだだけなので、今度細かくテキスト文書を抽出したいと思います。

21

Welcome to my other publishing channels