Linux->Windows主機目錄和文件名中文亂碼恢復 - clover_toeic - 博客園

Linux->Windows主機目錄和文件名中文亂碼恢復 - clover_toeic - 博客園

https://www.cnblogs.com/clover-toeic/p/5235316.html

中國知網文件亂碼修復方法。

Linux->Windows主機目錄和文件名中文亂碼恢復

標籤: 字符編碼 Python


[TOC]

聲明

本文主要記述作者如何通過Python腳本恢復跨平臺傳輸導致的目錄和文件名中文亂碼。作者對Python編程和字符編碼瞭解不多,紕漏難免,歡迎指正。同時,本文兼做學習筆記,存在囉嗦之處,敬請諒解。

本文同時也發佈於作業部落,閱讀體驗可能更好。

一. 亂碼問題

一年前,作者將Windows XP系統主機下創建的一批文件(以多級目錄組織),通過Samba手工拷貝至Linux系統主機,能正常顯示目錄和文件名中包含的中文字符。然後,通過filezilla連接Linux主機,將上述文件下載至移動硬盤,這個過程中也未出現亂碼。但將移動硬盤連接到Windows 7系統主機上時,卻發現目錄和文件名中包含的中文字符出現亂碼。

例如,文件名"GNU Readline庫函數的應用示例"和"守護進程接收終端輸入的一種變通性方法"分別顯示爲"GNU Readline库函数的应用示例"和"守护进程接收终端输入的一种变通性方法"。

但除目錄和文件名出現中文亂碼外,文件內容並無亂碼。

作者當時並不熟悉字符編碼知識,於是請教《通俗易懂地解決中文亂碼問題(1) --- 跨平臺亂碼》一文的作者Roly-Poly。Roly-Poly非常熱心地轉換了上述兩個文件名,並給出效果圖:
                          recode
以及相應的Java轉換方法:

String str = new String(new String(messyName.getBytes("ISO-8859-1"), "GBK").getBytes("GBK"), "UTF-8");

其中,messyName對應出現亂碼的字符串。getBytes(charset)將Unicode編碼存儲的字符串按照charset編碼,並以字節數組表示;new String(bytes[], charset)則將字節數組按照charset編碼進行組合識別,最後轉換爲Unicode存儲。因此,上述代碼表示先將當前編碼從 ISO-8859-1轉爲GBK,然後再從GBK轉爲UTF-8。

當然,Roly-Poly的轉換仍有缺憾,畢竟還存在未能正確解析的亂碼。"幸運"的是,當時出於謹慎,作者分別通過dir /S pathtree /F path(Windows)和ls -lRS --time-style=long-iso(Linux)創建了三份文件列表。這樣,在Roly-Poly轉碼的基礎上再做些校驗,有望替換爲完全正確的文件名。

然而,一方面因爲作者對Java語言和字符編碼比較陌生(主要是懶),另一方面因爲解析文件列表並更名的工作量預期較大,作者一直未付諸實踐。直到最近,纔開始從頭着手處理亂碼問題。這一過程學到不少知識,也走過不少彎路。教訓就是:凡事要一鼓作氣!

二. 調試環境

作者使用Python 2.7自帶的IDLE進行編碼調試。除非特別說明,本文所有代碼均爲Python語言。
參考Python字符編碼詳解一文,獲取當前環境的默認編碼:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, locale
def SysCoding():
    fmt = '{0}:{1}'
    #當前系統所使用的默認字符編碼
    print fmt.format('DefaultEncoding      ', sys.getdefaultencoding())
    #轉換Unicode文件名至系統文件名時所用的編碼('None'表示使用系統默認編碼)
    print fmt.format('FileSystemEncoding   ', sys.getfilesystemencoding())
    #默認的區域設置並返回元祖(語言, 編碼)
    print fmt.format('DefaultLocale        ', locale.getdefaultlocale())
    #用戶首選的文本數據編碼(猜測結果)
    print fmt.format('PreferredEncoding    ', locale.getpreferredencoding())

if __name__ == '__main__':
    SysCoding()

作者的Windows XP系統主機上,區域和語言選項->區域選項->標準和格式高級->非Unicode程序的語言均設置爲"中文(中國)";Windows 7系統主機上,區域和語言->格式管理->非Unicode程序的語言均設置爲"中文(簡體,中國)"。兩臺主機的SysCoding()輸出相同,均顯示如下:

DefaultEncoding      :ascii
FileSystemEncoding   :mbcs
DefaultLocale        :('zh_CN', 'cp936')
PreferredEncoding    :cp936

三. 目錄和文件名亂碼恢復

3.1 可選方案

3.1.1 通過合適的編解碼轉換

可使用chardet模塊detect()函數檢測給定字符的編碼。該函數返回檢測到的編碼'encoding'及其可信度'confidence'

安裝方法爲:命令提示符下執行C:\Python27\Scripts>easy_install.exe chardet後,自動下載egg文件包。若未安裝成功(import提示"ImportError: No module named chardet"),可到C:\Python27\Lib\site-packages目錄解壓egg文件包,將其中的chardet目錄(所有文件)拷貝到site-packages下面即可。

安裝成功後,按照以下方法檢測字符編碼:

#coding: gbk
import chardet
print chardet.detect('abc') #{'confidence': 1.0, 'encoding': 'ascii'}
print chardet.detect('喊')  #{'confidence': 0.73, 'encoding': 'windows-1252'}
print chardet.detect('漢')  #{'confidence': 0.99, 'encoding': 'TIS-620'} ##Thailand
print chardet.detect('漢中華人民共和國') #{'confidence': 0.99, 'encoding': 'GB2312'}
print '漢中華人民共和國', repr('漢中華人民共和國')
#漢中華人民共和國 '\xba\xba\xd6\xd0\xbb\xaa\xc8\xcb\xc3\xf1\xb9\xb2\xba\xcd\xb9\xfa'

可見,當字符"樣本"過少時,chardet檢測結果並不準確(如'漢'被識別爲泰文)。在Shell中執行上述檢測時,結果與之相同。

作爲對比,聲明爲coding: utf-8時,檢測結果又是另一番"景象":

import chardet
print chardet.detect('abc') #{'confidence': 1.0, 'encoding': 'ascii'}
print chardet.detect('成CUnit进è¡') #{'confidence': 0.99, 'encoding': 'utf-8'}
print chardet.detect('喊')  #{'confidence': 0.73, 'encoding': 'windows-1252'}
print chardet.detect('漢')  #{'confidence': 0.73, 'encoding': 'windows-1252'}
print chardet.detect('漢中華人民共和國') #{'confidence': 0.99, 'encoding': 'utf-8'}
print '漢中華人民共和國', repr('漢中華人民共和國')
#奼変腑鍗庝漢姘戝叡鍜屽浗 '\xe6\xb1\x89\xe4\xb8\xad\xe5\x8d\x8e\xe4\xba\xba\xe6\xb0\x91\xe5\x85\xb1\xe5\x92\x8c\xe5\x9b\xbd'

可見,'口语奇招_新æµ'被檢測爲UTF-8編碼。這是因爲UTF-8是ASCII的超集。當字符串序列中所有字符均爲ASCII符號(前128個字符)時,chardet認爲該串爲ASCII編碼;當字符串序列中也含有所有擴展ASCII符號時,chardet很可能認爲該串爲UTF-8編碼。此外,print根據本地操作系統默認字符編碼(GBK),將'漢中華人民共和國'打印爲奼変腑鍗庝漢姘戝叡鍜屽浗

3.1.2 根據文件列表信息匹配

通過正則表達式提取文件列表中的目錄大小、文件數目、文件名及其大小、創建時間等信息,再遍歷移動硬盤亂碼目錄,進行匹配和更名。爲保險起見,應維護一份映射文件,存儲文件路徑、原名和新名,以便恢復或校正。

爲減少匹配和更名次數,只操作名稱包含字母數字以外字符的目錄和文件。此外,還可對文件列表排序,如dir path /S /O:S(按大小升序排列)。

3.1.3 機器學習

因爲word、網頁等文件打開後通常可以看到標題,作者得以整理若干文件亂碼名與正常名的映射數據。這樣,藉助機器學習(如基於實例的算法),最終有望消除所有亂碼。

顯然,這一方案難度太高,並不現實。

3.2 逐步實踐

3.2.1 獲取單級目錄及其文件信息

首先,創建名稱正常的多級目錄供調試用。之所以不用原始亂碼目錄調試,是因爲一旦測試失敗很可能會破壞"樣本"。

然後,通過以下代碼獲取單級目錄的大小、文件數目、文件名及大小、創建時間等信息:

import time
from os.path import join, getsize, getmtime, getctime
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
#os.getcwd()返回當前工作目錄
def FilesInfo():
    for root, dirs, files in os.walk(CURRENT_DIR):
        for file in files:
            path = join(root, file)
            ctime = time.ctime(getctime(path)) #創建時間
            print 'Name:%-12s Size:%-7s Ctime:%s' %(file, getsize(path), ctime)

        print root, "consumes",
        print sum(getsize(join(root, file)) for file in files),
        print "bytes in", len(files), "non-directory files!"

注意,當本模塊由其他模塊import並執行時,os.getcwd()返回的並非本模塊目錄。

執行FilesInfo()後,輸出結果如下:

Name:Coding.py    Size:7936    Ctime:Mon Feb 29 09:41:15 2016
Name:d_res.bmp    Size:522534  Ctime:Mon Feb 29 09:41:15 2016
Name:error3.bmp   Size:70782   Ctime:Mon Feb 29 09:41:15 2016
Name:Open.bmp     Size:354746  Ctime:Mon Feb 29 09:41:15 2016
Name:Thumbs.db    Size:19968   Ctime:Mon Feb 29 09:41:47 2016
Name:typec.bmp    Size:199022  Ctime:Mon Feb 29 09:41:15 2016
Name:WalkDir.py   Size:4894    Ctime:Mon Feb 29 09:41:15 2016
Name:復Coding.py  Size:6564    Ctime:Mon Feb 29 15:41:37 2016
E:\PyTest\stuff consumes 1186446 bytes in 8 non-directory files!

3.2.2 從文件列表中提取目錄和文件信息

在命令提示符下dir \F出調試目錄的結構。截取部分如下:

C:\Program Files\IDM Computer Solutions\UEStudio>e:

 E:\PyTest 的目錄

2016-02-24  11:53    <DIR>          .
2016-02-24  11:53    <DIR>          ..
2016-02-23  17:22             1,434 backup_ver2.py

然後,通過ParseFileList()函數解析出"E:\PyTest"之類的路徑:

import codecs, re
def ParseFileList():
    #Windows記事本默認的字符編碼爲"ANSI"(實際是GBK)
    file = codecs.open(r'E:\PyTest\filelist.txt', encoding='gbk')
    for line in file:
        #下句等效於m = re.match(u' (.+) 的目錄\s*$', line)
        m = re.compile(u"""             # Python默認字符編碼爲Ansi, 需加u轉爲Unicode
                            (.+) 的目錄  # 將' 的目錄'前的部分作爲分組(Group)
                            \s*$        # 行尾""", re.X).match(line)
        if m != None:
            print m.groups()[0]

注意,此處並未使用內置的open()方法打開文件。因爲該方法得到的line爲str類型,需要使用正確的編碼格式進行decode(),否則將無法匹配到"的目錄"。而codecs.open()方法打開文件時讀取的就是Unicode類型,不容易出現編碼問題。

當然,若將源代碼文件中的字符編碼聲明改爲#coding=gbk,並去掉pattern字符串前綴u,則使用內置的open()方法仍可匹配到"的目錄"

目錄大小、文件數目、文件名及其大小等,均可通過合適的正則表達式提取。然而,作者很快意識到,根據文件列表遍歷和更名的方案實現起來過於複雜。於是,放棄正則匹配的嘗試。

3.2.3 遍歷目錄並更名

雖然文件列表正則匹配的方案不可行,但遍歷和更名卻是所有方案所必需的。

Python中有三種遍歷目錄的方法,即os.listdir()os.walk()os.path.walk()。這三者中,作者首選os.walk()方法,遍歷代碼如下:

import os
def ValidateDir(dirPath):
    #判斷路徑是否爲Unicode。若否,將其轉換爲Unicode編碼
    if isinstance(dirPath, unicode) == False:
        #下句等效於dirPath = dirPath.decode('utf8')
        dirPath = unicode(dirPath, 'utf8')

    #判斷路徑是否存在(不區分大小寫)
    if os.path.exists(dirPath) == False:
        print dirPath + ' is non-existent!'
        return ''

    #判斷路徑是否爲目錄(不區分大小寫)
    if os.path.isdir(dirPath) == False:
        print dirPath + ' is not a directory!'
        return ''
    return dirPath

def WalkDirReport(dirPath, fileNum):
    print '##############' + str(fileNum) + ' files processed##############'

def WalkDir(dirPath):
    dirPath = ValidateDir(dirPath)
    if not dirPath:
        return

    #遍歷路徑下的文件及子目錄
    fileNum = 0
    for root, dirs, files in os.walk(dirPath):
        for file in files:
            #處理文件
            #ChangeNames(root, file)
            #RestoreNames(root, file)

            fileNum += 1
            while(fileNum % 100) == 0:
                prompt = '$' + str(fileNum) + ' files processed, ' \
                         + '''pause for checking. Type 'c' to continue: '''
                if raw_input(prompt) == 'c':
                    break
    WalkDirReport(dirPath, fileNum)

其中,ValidateDir()用於校驗路徑合法性,同時還將非Unicode路徑轉爲Unicode路徑(該步驟也可由使用者自行完成)。

os.listdir()方法遍歷目錄則較爲"笨拙",對比如下:

def WalkDir_unsafe(dirPath):
    dirPath = ValidateDir(dirPath)
    if not dirPath:
        return

    #遍歷路徑下的文件及子目錄
    fileNum = 0
    nameList = os.listdir(dirPath)
    for name in nameList:
        path = os.path.join(dirPath, name)
        #類型爲目錄,遞歸(存在棧溢出風險)
        if os.path.isdir(path) == True:
            fileNum += WalkDir_unsafe(path)
            continue

        #處理文件
        '''此時name等效於os.path.basename(path),即文件名;
           dirPath等效於os.path.dirname(path),即目錄名;
           path等效於os.path.abspath(os.path.basename(path)),即絕對路徑'''
           fileNum += 1
    return fileNum

因爲採用遞歸處理,所以該方法存在棧溢出風險(不過作者尚未遇到這種情況)。使用時,需按照如下方式調用:

WalkDirReport(r'E:\Pytest\測試', WalkDir_unsafe(r'E:\Pytest\測試'))

注意,雖然os.walk()本身仍由os.listdir()遞歸實現,但卻是生成器(generator)寫法,相比普通遞歸更節省內存資源。

遍歷目錄調試通過後,就可着手實現目錄和文件更名。爲簡單起見,更名規則爲"尾部添0",即"E:\a\b.txt"會轉換爲"E:\a0\b.txt0"。同時提供恢復函數,以便反覆調試。代碼如下:

def ChangeNames(dir, file):
    #將'E:\a\b.txt'轉換爲'E:\a0\b.txt0',以此類推
    filePath = os.path.join(dir, file)
    newdir = dir.split('\\')
    newdir[1:] = map(lambda x: x+'0', newdir[1:]) #盤符不變
    newdir = '\\'.join(newdir)
    ufilePath = os.path.join(newdir, file) + '0'
    print filePath + ' => ' + ufilePath
    os.renames(filePath, ufilePath)

def RestoreNames(dir, file):
    #將'E:\a0\b.txt0'恢復爲'E:\a\b.txt',以此類推
    filePath = os.path.join(dir, file)
    newdir = dir.split('\\')
    newdir[1:] = map(lambda x: x[:-1], newdir[1:])
    newdir = '\\'.join(newdir)
    ufilePath = os.path.join(newdir, file)[:-1]
    print filePath + ' => ' + ufilePath
    os.renames(filePath, ufilePath)

可見,"添0"和恢復的方法比較"笨拙"。但作爲Python新手,作者暫時只能如此。結合遍歷代碼,ChangeNames()RestoreNames()可有效地更名和恢復。

注意os.renames()方法,該方法可對嵌套目錄及其文件更名,可能會創建臨時目錄以存放新命名的子目錄和文件。因此,若以WalkDir(r'E:\bPytest')方式調用且bPytest目錄下存在空的子目錄,則更名後該子目錄保持原名原位置(仍在E:\bPytest目錄下),而其他子目錄及文件被更名且"轉移"到E:\bPytest0目錄下——同時出現bPytestbPytest0兩個目錄!

3.2.4 消除單個文件名亂碼

遍歷和更名調試成功後,接下來便是重中之重——亂碼文件名恢復。

瀏覽各種網絡資料後,作者終於在Stackoverflow網站上一則問答Getting correct utf8 Chinese characters from messed-up iso-8859-1 in Python and MySQL裏找到一線曙光。回答者通過u'最'.encode('cp1252').decode('utf8')成功地將最轉換爲"最"——這與作者遇到的亂碼何其相似!

在嘗試cp1252cp1254...等衆多Windows編碼後,作者終於找到正確的編碼格式,即latin_1,別名iso-8859-1iso8859-18859cp819latinlatin1L1。測試代碼如下:

print u'项目中集成CUnit进行开发测试'.encode('latin_1').decode('utf8')
print u'《NBC夜间新闻(2011-2季)'.encode('latin_1').decode('utf8')
print '新浪博客'.decode('utf8').encode('latin_1').decode('utf8')

其中,u'string'等效於'string'.decode('utf8')。運行結果爲:

項目中集成CUnit進行開發測試
《NBC夜間新聞(2011-2季)
新浪博客

經人工檢驗,完全符合期望!

此時再回想Roly-Poly提供的Java轉碼語句:

String str = new String(new String(messyName.getBytes("ISO-8859-1"), "GBK").getBytes("GBK"), "UTF-8");

因爲字符串在Java內存中以Unicode編碼存儲,且getBytes()new String()分別對應Python裏的encode()decode(),所以等效的Python轉碼語句如下:

str = u'messyName'.encode('latin_1').decode('gbk').encode('gbk').decode('utf8')

從字符編碼規則可知,經過GBK編解碼"中轉"後,很可能出現data loss。以第一章的兩個文件名爲例,其Python轉碼如下:

s1 = u'GNU Readline库函数的应用示例'.encode('latin_1').decode('gbk').encode('gbk','replace').decode('utf8')
s2 = u'守护进程接收终端输入的一种变通性方法'.encode('latin_1').decode('gbk','replace').encode('gbk','replace').decode('utf8','replace')
s3 = u'守护进程接收终端输入的一种变通性方法'.encode('latin_1').decode('utf8')
print s1  #GNU Readline庫函數的應用示例
print s2  #守護進程接收終�?輸入的一種變通�?方法
print s3  #守護進程接收終端輸入的一種變通性方法

其中,'replace'參數以適當的字符替換編解碼過程中無法識別的字符,否則將產生UnicodeDecodeErrorUnicodeEncodeError異常。可見,就本文問題而言,並不需要GBK編解碼"中轉"。

3.2.5 消除單級目錄下文件名亂碼

文件名字符串亂碼恢復成功後,接下來將對文件更名。該步驟需要在掛接移動硬盤的Windows 7主機上進行,因爲在Windows XP主機上文件名亂碼無法以期望的方式顯示和解析。例如:
                     display
最後那個以亂碼字符串爲名創建的文件,無法打開(UEStudio提示"含有一個無效的路徑",記事本提示"無效的窗口句柄"),os.rename()也會報錯。

在Windows 7主機上,作者從移動硬盤拷貝一個單級亂碼目錄至磁盤作調試用。然後,編寫代碼恢復該目錄下的所有亂碼文件名:

from nt import chdir
def RecodeName(dirPath):
    nameList = os.listdir(dirPath)
    for fileName in nameList:
        try:
            ufileName = fileName.encode('latin_1').decode('utf8')
        except UnicodeEncodeError as e:
            print '[e]' + fileName + '(Possibly needn\'t xcode!)'
            continue
        print (fileName + ' => ' + ufileName)
        #rename之前要先用chdir()函數進入到目標文件所在的路徑
        chdir(dirPath)
        os.rename(fileName, ufileName)

因爲存在某些文件已被手工更名的可能性,encode('latin_1')時會拋出UnicodeEncodeError異常,所以需要跳過這些文件。

運行結果如下:

8.26南京990小经验,希望对以后的人有帮助 - 英语托业考试(TOEIC) - 大家论坛 -.url => 8.26南京990小經驗,希望對以後的人有幫助 - 英語託業考試(TOEIC) - 大家論壇 -.url
~2011.5.29~托业905感想~感谢大家网论坛~希望能给大家带来帮助 - 英语托业考试(TOEIC) - 大家论坛 -.url => ~2011.5.29~託業905感想~感謝大家網論壇~希望能給大家帶來幫助 - 英語託業考試(TOEIC) - 大家論壇 -.url
中国雅思网.url => 中國雅思網.url
如何提高英语听力_提高英语听力的方法_听力课堂.url => 如何提高英語聽力_提高英語聽力的方法_聽力課堂.url
>>> 

經驗證,指定目錄下所有亂碼文件名均正確恢復。

3.2.6 消除嵌套目錄名及其文件名亂碼

單級目錄下文件名亂碼消除後,作者換用os.renames()方法消除嵌套目錄名及其文件名。代碼如下:

def RecodeNames(dir, file):
    filePath = os.path.join(dir, file)
    try:
        filePathNew = filePath.encode('latin_1').decode('utf8')
    except UnicodeEncodeError as e:
        '''os.renames()會創建臨時目錄以存放新命名的子目錄和文件,故此處異常
           表明最底層文件已解碼成功(可能是手工處理),但其目錄路徑仍未解碼。
           因此,需構造已解碼全路徑,以便renames將上述文件拷貝至新目錄。
        '''
        print '[e]' + filePath + '(Possibly needn\'t xcode!)'
        filePathNew = os.path.join(dir.encode('latin_1').decode('utf8'), file)

    print (filePath + ' => ' + filePathNew)
    os.renames(filePath, filePathNew)

WalkDir(r'F:\Pytest\Study')運行,結果如下:

F:\Pytest\Study\John_新浪博客.url => F:\Pytest\Study\John_新浪博客.url
[e]F:\Pytest\Study\ABR\~2011.5.29~託業905感想~感謝大家網論壇~希望能給大家帶來幫助 - 英語託業考試(TOEIC) - 大家論壇 -.url(Possibly needn't xcode!)
F:\Pytest\Study\ABR\~2011.5.29~託業905感想~感謝大家網論壇~希望能給大家帶來幫助 - 英語託業考試(TOEIC) - 大家論壇 -.url => F:\Pytest\Study\ABR\~2011.5.29~託業905感想~感謝大家網論壇~希望能給大家帶來幫助 - 英語託業考試(TOEIC) - 大家論壇 -.url
F:\Pytest\Study\待读\《NBC夜间新闻(2011-2å­£)》(NBC Nightly News)([m4v]英语听力下载 -学习资料库.url => F:\Pytest\Study\待讀\《NBC夜間新聞(2011-2季)》(NBC Nightly News)([m4v]英語聽力下載 -學習資料庫.url
##############3 files processed##############

可見,根目錄和子目錄及所有下屬文件均正確更名。

讀者可能已經注意到,上述根目錄路徑名均爲英文字符。假如該路徑包含已解碼的中文目錄名(可能是手工處理)呢?例如將Study目錄名改爲外語學習。顯然,encode('latin_1')時會觸發UnicodeEncodeError異常。因此,編解碼時需要跳過這些中文目錄名:

def RecodeNames(dir, file):
    filePath = os.path.join(dir, file)
    #對路徑解碼
    paths = filePath.split('\\')
    for i in range(len(paths)):
        try:
            paths[i] = paths[i].encode('latin_1').decode('utf8')
        except UnicodeEncodeError as e:
            #路徑可能出現已解碼的中文(可能是手工處理),不再重複解碼
            continue

    filePathNew = '\\'.join(paths)
    print (filePath + ' => ' + filePathNew)
    os.renames(filePath, filePathNew)

什麼?假如目錄名包含部分亂碼部分中文字符?拜託,這種情況不可能出現。

3.2.7 恢復原始亂碼目錄和文件名

激動人心的時刻來到了!本節將正式處理移動硬盤中的原始亂碼目錄和文件名。基於以上調試結果,最終的亂碼恢復代碼如下:

failedNamesList = []
def RecordFailedNames(name):
    failedNamesList.append(name)

import ctypes
def RecodeNames(dir, file):
    filePath = os.path.join(dir, file)
    #對路徑解碼
    paths = filePath.split('\\')
    for i in range(len(paths)):
        try:
            paths[i] = paths[i].encode('latin_1').decode('utf8')
        except UnicodeEncodeError as e:
            #路徑可能出現已解碼的中文(可能是手工處理),不再重複解碼
            continue

    filePathNew = '\\'.join(paths)
    print (filePath + ' => ' + filePathNew)
    try:
        os.renames(filePath, filePathNew)
    except WindowsError as e:
        print "[e]WindowsError({0}): {1}".format(e.winerror, ctypes.FormatError(e.winerror))
        RecordFailedNames(filePath)

def WalkDirReport(dirPath, fileNum):
    print '##############' + str(fileNum) + ' files processed##############'
    print 'Failed files(%d):' %(len(failedNamesList))
    for i in range(len(failedNamesList)):
        print '  ' + failedNamesList[i]

某些亂碼文件名過長(多爲.mht文件),os.renames()時會觸發WindowsError[3]異常,如:
                    error3
事實上,在資源管理器裏打開這些文件時,會提示"源路徑過長":
                    open
上述代碼可有效地恢復原始亂碼目錄和文件名。當時的運行截圖如下:
                    typec
以及
                    d_res
經初步檢驗,更名成功。此時可通過tree命令生成生成目錄樹,與原先保存的文件列表對比。遺憾的是,兩者樹狀列表順序不同,無法直接對比。當然,如果夠閒,也可以編程比照。

四. 後記

本文所述的亂碼恢復實踐其實是種"摸着石頭過河(trial and error)"的過程。如果當初先研究字符編碼,而不是左右開弓,應該會避免不少彎路。當然論及性價比,孰優孰劣,亦未可知。
最後,關於Python字符編碼基礎知識,作者將在後續文章中加以說明,同時澄清本文一些謬誤之處。臉皮薄,就不說"敬請期待"了~~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章