精华内容
下载资源
问答
  • Linux->Windows主机目录和文件名中文乱码恢复 - clover_toeic - 博客园 https://www.cnblogs.com/clover-toeic/p/5235316.html 中国知网文件乱码修复方法。 Linux->Windows主机目录和文件名中文乱码恢复 ...
    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字符编码基础知识,作者将在后续文章中加以说明,同时澄清本文一些谬误之处。脸皮薄,就不说"敬请期待"了~~


    展开全文
  • 1、中文名文件上传后保存在linux服务器上文件名会乱码,但是我们通过SSH直接对服务器上的一个文件进行重命名是可以使用中文的,而且显示出来是正确的,这说明服务器是可以支持中文的。 2、而为什么上传的中文名文件...

    1、中文名文件上传后保存在linux服务器上文件名会乱码,但是我们通过SSH直接对服务器上的一个文件进行重命名是可以使用中文的,而且显示出来是正确的,这说明服务器是可以支持中文的。

    2、而为什么上传的中文名文件保存起来以后文件名会乱码呢?这是因为Windows的默认编码为GBK,Linux的默认编码为UTF-8。在Windows下编辑的中文,上传到Linux下就会显示为乱码。为了解决此问题,修改Linux的默认编码为GBK,就能够成功的解决乱码问题。


    首先运行locale查看本地编码方式:

    方式一:

    [root@localhost hins]# locale

    LANG=en_US.UTF-8
    LC_CTYPE="en_US.UTF-8"
    LC_NUMERIC="en_US.UTF-8"
    LC_TIME="en_US.UTF-8"
    LC_COLLATE="en_US.UTF-8"
    LC_MONETARY="en_US.UTF-8"
    LC_MESSAGES="en_US.UTF-8"
    LC_PAPER="en_US.UTF-8"
    LC_NAME="en_US.UTF-8"
    LC_ADDRESS="en_US.UTF-8"
    LC_TELEPHONE="en_US.UTF-8"
    LC_MEASUREMENT="en_US.UTF-8"
    LC_IDENTIFICATION="en_US.UTF-8"
    LC_ALL=

    方式二:

    [root@localhost hins]# echo $LANG
    zh_CN.GBK

    这是服务器默认的编码,根据后面的方法修改后应该为:
    (我修改为GBK以后的linux服务器的编码)


    方法如下:

    方法1:(试了一下,编码方式没有改变,可能是要重启服务器才能生效)
    vi /etc/sysconfig/i18n
    默认为:
    LANG="en_US.UTF-8"
    SYSFONT="latarcyrheb-sun16"


    修改为:
    LANG="zh_CN.GBK"
    SUPPORTED="zh_CN.UTF-8:zh_CN:zh"
    SYSFONT="latarcyrheb-sun16"


    方法2:(推荐这种方法,不用重启服务器)
    vi /etc/profile

    export LC_ALL="zh_CN.GBK"
    export LANG="zh_CN.GBK"

    生效:source /etc/profile


    [root@localhost hins]# locale
    LANG=zh_CN.GBK
    LC_CTYPE="zh_CN.GBK"
    LC_NUMERIC="zh_CN.GBK"
    LC_TIME="zh_CN.GBK"
    LC_COLLATE="zh_CN.GBK"
    LC_MONETARY="zh_CN.GBK"
    LC_MESSAGES="zh_CN.GBK"
    LC_PAPER="zh_CN.GBK"
    LC_NAME="zh_CN.GBK"
    LC_ADDRESS="zh_CN.GBK"
    LC_TELEPHONE="zh_CN.GBK"
    LC_MEASUREMENT="zh_CN.GBK"
    LC_IDENTIFICATION="zh_CN.GBK"
    LC_ALL=zh_CN.GBK


    运行locale指令得到当前系统编码设置的详细资料。

    一、locale的五脏六腑


    1、 语言符号及其分类(LC_CTYPE)
    2、 数字(LC_NUMERIC)
    3、 比较和排序习惯(LC_COLLATE)
    4、 时间显示格式(LC_TIME)
    5、 货币单位(LC_MONETARY)
    6、 信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES)
    7、 姓名书写方式(LC_NAME)
    8、 地址书写方式(LC_ADDRESS)
    9、 电话号码书写方式(LC_TELEPHONE)
    10、度量衡表达方式(LC_MEASUREMENT)
    11、默认纸张尺寸大小(LC_PAPER)
    12、对locale自身包含信息的概述(LC_IDENTIFICATION)。


    二、理解locale的设置


    设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量:LC_ALL和LANG。


    它们之间有一个优先级的关系:LC_ALL > LC_* > LANG


    可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。


    三 具体设定locale的方法(zh_CN.UTF-8、zh_CN.GBK)


    freebsd的设置:


    1.GDM登录改为终端登录后startx启动图形桌面


    2.在~/.cshrc中增加如下语句,(根据自己使用的shell进行相应设置)


    setenv LANG zh_CN.GBK
    setenv LC_ALL zh_CN.GBK
    setenv LC_CTYPE zh_CN.GBK


    3.修改/etc/fstab的默认值:


    linux 设置:


    1.修改/etc/sysconfig/i18n文件,LANG="zh_CN.UTF-8"或LANG="zh_CN.GBK"


    普通用户修改~/.profile


    ...
    export LANG zh_CN.GBK
    ...


    2.修改/etc/fstab的默认值
     

    展开全文
  • 批处理当前目录的文件名1.1. 打开 Windows 控制台(cmd)1.2. 当前目录打开 Windows 控制台(cmd)1.3. 建议使用第三方控制台 cmder1.4. 获取当前目录的文件名1.5. 用 `Excel`打开 `rename.xls`1.6. 构建重命名脚本...

    1. 批处理当前目录的文件名重命名

    1.1. 打开 Windows 控制台(cmd)

    • Windows 键 +R
    • 输入 cmd回车
    • 即可打开 Windows 的控制台

    1.2. 当前目录打开 Windows 控制台(cmd)

    • 在当前目录按住 Shift 键 +鼠标左键
    • 在右键选项上选择cmdpowershell回车
    • 即可打开 Windows 的控制台/powershell

    1.3. 建议使用第三方控制台 cmder

    • 解压 cmder.7z 到软件安装的目录
    • 使用管理员权限运行 cmder.exe
    • 注册cmder,在 cmder 终端中输入:
    cmder.exe /register ALL
    

    1.4. 获取当前目录的文件名

    cmdpowershellcmder中运行

    dir /b > rename.xls
    

    /b 是使用空格间隔

    获取 dir 帮助

    dir /?
    显示目录中的文件和子目录列表。
    
    DIR [drive:][path][filename] [/A[[:]attributes]] [/B] [/C] [/D] [/L] [/N]
      [/O[[:]sortorder]] [/P] [/Q] [/R] [/S] [/T[[:]timefield]] [/W] [/X] [/4]
    
      [drive:][path][filename]
                  指定要列出的驱动器、目录和/或文件。
    
      /A          显示具有指定属性的文件。
      属性         D  目录                R  只读文件
                   H  隐藏文件            A  准备存档的文件
                   S  系统文件            I  无内容索引文件
                   L  重新分析点          O  脱机文件
                   -  表示“否”的前缀
      /B          使用空格式(没有标题信息或摘要)。
      /C          在文件大小中显示千位数分隔符。这是默认值。用 /-C 来
                  禁用分隔符显示。
      /D          跟宽式相同,但文件是按栏分类列出的。
      /L          用小写。
      /N          新的长列表格式,其中文件名在最右边。
      /O          用分类顺序列出文件。
      排列顺序     N  按名称(字母顺序)     S  按大小(从小到大)
                   E  按扩展名(字母顺序)   D  按日期/时间(从先到后)
                   G  组目录优先           -  反转顺序的前缀
      /P          在每个信息屏幕后暂停。
      /Q          显示文件所有者。
      /R          显示文件的备用数据流。
      /S          显示指定目录和所有子目录中的文件。
      /T          控制显示或用来分类的时间字符域
      时间段      C  创建时间
                  A  上次访问时间
                  W  上次写入的时间
      /W          用宽列表格式。
      /X          显示为非 8dot3 文件名产生的短名称。格式是 /N 的格式,
                  短名称插在长名称前面。如果没有短名称,在其位置则
                  显示空白。
      /4          以四位数字显示年份
    
    可以在 DIRCMD 环境变量中预先设定开关。通过添加前缀 - (破折号)
    来替代预先设定的开关。例如,/-W。
    

    如果需要指定排序方式的话,根据帮助提示可以使用 /o 选项,即:

    dir /b /on > rename.xls
    dir /b /os > rename.xls
    dir /b /oe > rename.xls
    dir /b /od > rename.xls
    dir /b /og > rename.xls
    
    dir /b /o-n > rename.xls
    dir /b /o-s > rename.xls
    dir /b /o-e > rename.xls
    dir /b /o-d > rename.xls
    dir /b /o-g > rename.xls
    
    

    1.5. 用 Excel打开 rename.xls

    默认 A 列当前目录的文件名
    B 列设置新文件名
    C 列用下面的公式

    // A1 原文件名
    // B1 新文件名
    
    ="ren "&A1&""&B1&""
    

    renrename (重命名) 的缩写

    1.6. 构建重命名脚本

    • 复制 C 列 在表格空白地方用 粘贴值的放置粘贴
    • 新建一个空的文本文件,改名和扩展名为 rename.bat,并用记事本或文本编辑器打开
    • 粘贴值的内容复制打开的 rename.bat
    • "无法在 cmd 中被正确处理还需要将 " 替换为空
    • 使用文本编辑器的替换功能将 " 替换为 (空),并保存 rename.bat 文件,

    1.7. 执行批处理

    双击 rename.bat 即可执行批处理脚本。

    展开全文
  • 第十章、如何给10^7个数据量的磁盘文件排序作者:July,yansha,5,编程艺术室。出处:http://blog.csdn.net/v_JULY_v 。 前奏 经过几天的痛苦沉思,最终决定,把原程序员面试题狂想曲系列正式更名为程序员编程...

                     第十章、如何给10^7个数据量的磁盘文件排序


    作者:July,yansha,5,编程艺术室。
    出处:http://blog.csdn.net/v_JULY_v 

     

    前奏

        经过几天的痛苦沉思,最终决定,把原程序员面试题狂想曲系列正式更名为程序员编程艺术系列,同时,狂想曲创作组更名为编程艺术室。之所以要改名,我们考虑到三点:1、为面试服务不能成为我们最终或最主要的目的,2、我更愿把解答一道道面试题,ACM题等各类程序设计题目的过程,当做一种艺术来看待,3、艺术的提炼本身是一个非常非常艰难的过程,但我们乐意接受这个挑战。

        ok,如果任何人对本编程艺术系列有任何意见,或发现了本编程艺术系列任何问题,漏洞,bug,欢迎随时提出,我们将虚心接受并感激不尽,以为他人创造更好的价值,更好的服务。

     

    第一节、如何给磁盘文件排序
    问题描述:
    输入:给定一个文件,里面最多含有n个不重复的正整数(也就是说可能含有少于n个不重复正整数),且其中每个数都小于等于n,n=10^7。
    输出:得到按从小到大升序排列的包含所有输入的整数的列表。
    条件:最多有大约1MB的内存空间可用,但磁盘空间足够。且要求运行时间在5分钟以下,10秒为最佳结果。

    分析:下面咱们来一步一步的解决这个问题,
        1、归并排序。你可能会想到把磁盘文件进行归并排序,但题目要求你只有1MB的内存空间可用,所以,归并排序这个方法不行。
        2、位图方案。熟悉位图的朋友可能会想到用位图来表示这个文件集合。例如正如编程珠玑一书上所述,用一个20位长的字符串来表示一个所有元素都小于20的简单的非负整数集合,边框用如下字符串来表示集合{1,2,3,5,8,13}:

    0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0

    上述集合中各数对应的位置则置1,没有对应的数的位置则置0。

        参考编程珠玑一书上的位图方案,针对我们的10^7个数据量的磁盘文件排序问题,我们可以这么考虑,由于每个7位十进制整数表示一个小于1000万的整数。我们可以使用一个具有1000万个位的字符串来表示这个文件,其中,当且仅当整数i在文件中存在时,第i位为1。采取这个位图的方案是因为我们面对的这个问题的特殊性:1、输入数据限制在相对较小的范围内,2、数据没有重复,3、其中的每条记录都是单一的整数,没有任何其它与之关联的数据。
        所以,此问题用位图的方案分为以下三步进行解决:

    • 第一步,将所有的位都置为0,从而将集合初始化为空。
    • 第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。
    • 第三步,检验每一位,如果该位为1,就输出对应的整数。

        经过以上三步后,产生有序的输出文件。令n为位图向量中的位数(本例中为1000 0000),程序可以用伪代码表示如下:

    //磁盘文件排序位图方案的伪代码
    //copyright@ Jon Bentley
    //July、updated,2011.05.29。
    
    //第一步,将所有的位都初始化为0
    for i ={0,....n}    
       bit[i]=0;
    //第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。
    for each i in the input file   
       bit[i]=1;
    
    //第三步,检验每一位,如果该位为1,就输出对应的整数。
    for i={0...n}    
      if bit[i]==1      
        write i on the output file

        上面只是为了简单介绍下位图算法的伪代码之抽象级描述。显然,咱们面对的问题,可不是这么简单。下面,我们试着针对这个要分两趟给磁盘文件排序的具体问题编写完整代码,如下。

    //copyright@ yansha
    //July、2010.05.30。
    //位图方案解决10^7个数据量的文件的排序问题
    //如果有重复的数据,那么只能显示其中一个 其他的将被忽略
    #include <iostream>
    #include <bitset>
    #include <assert.h>
    #include <time.h>
    using namespace std;
    
    const int max_each_scan = 5000000;
    
    int main()
    {
    	clock_t begin = clock();
    	bitset<max_each_scan> bit_map;
    	bit_map.reset();
    	
    	// open the file with the unsorted data
    	FILE *fp_unsort_file = fopen("data.txt", "r");
    	assert(fp_unsort_file);
    	int num;
    
    	// the first time scan to sort the data between 0 - 4999999
    	while (fscanf(fp_unsort_file, "%d ", &num) != EOF)
    	{
    		if (num < max_each_scan)
    			bit_map.set(num, 1);
    	}
    	
    	FILE *fp_sort_file = fopen("sort.txt", "w");
    	assert(fp_sort_file);
    	int i;
    	
    	// write the sorted data into file
    	for (i = 0; i < max_each_scan; i++)
    	{
    		if (bit_map[i] == 1)
    			fprintf(fp_sort_file, "%d ", i);
    	}
    	
    	// the second time scan to sort the data between 5000000 - 9999999
    	int result = fseek(fp_unsort_file, 0, SEEK_SET);
    	if (result)
    		cout << "fseek failed!" << endl;
    	else
    	{
    		bit_map.reset();
    		while (fscanf(fp_unsort_file, "%d ", &num) != EOF)
    		{
    			if (num >= max_each_scan && num < 10000000)
    			{
    				num -= max_each_scan;
    				bit_map.set(num, 1);
    			}
    		}
    		for (i = 0; i < max_each_scan; i++)
    		{
    			if (bit_map[i] == 1)
    				fprintf(fp_sort_file, "%d ", i + max_each_scan);
    		}
    	}
    	
    	clock_t end = clock();
    	cout<<"用位图的方法,耗时:"<<endl;
    	cout << (end - begin) / CLK_TCK << "s" << endl;
    	fclose(fp_sort_file);
    	fclose(fp_unsort_file);
    	return 0;
    }

     而后测试了一下上述程序的运行时间,采取位图方案耗时14s,即14000ms:

    本章中,生成大数据量(1000w)的程序如下,下文第二节的多路归并算法的c++实现和第三节的磁盘文件排序的编程实现中,生成的1000w数据量也是用本程序产生的,且本章内生成的1000w数据量的数据文件统一命名为“data.txt”。

    //purpose:	生成随机的不重复的测试数据
    //copyright@ 2011.04.19 yansha
    //1000w数据量,要保证生成不重复的数据量,一般的程序没有做到。
    //但,本程序做到了。
    //July、2010.05.30。
    #include <iostream>
    #include <time.h>
    #include <assert.h>
    using namespace std;
    
    const int size = 10000000;
    int num[size];
    
    int main()
    {
    	int n;
    	FILE *fp = fopen("data.txt", "w");
    	assert(fp);
    
    	for (n = 1; n <= size; n++)  
    		//之前此处写成了n=0;n<size。导致下面有一段小程序的测试数据出现了0,特此订正。
    		num[n] = n;
    	srand((unsigned)time(NULL));
    	int i, j;
    
    	for (n = 0; n < size; n++)
    	{
    		i = (rand() * RAND_MAX + rand()) % 10000000;
    		j = (rand() * RAND_MAX + rand()) % 10000000;
    		swap(num[i], num[j]);
    	}
    
    	for (n = 0; n < size; n++)
    		fprintf(fp, "%d ", num[n]);
    	fclose(fp);
    	return 0;
    }

        不过很快,我们就将意识到,用此位图方法,严格说来还是不太行,空间消耗10^7/8还是大于1M(1M=1024*1024空间,小于10^7/8)。
        既然如果用位图方案的话,我们需要约1.25MB(若每条记录是8位的正整数的话,则10000000/(1024*1024*8) ~= 1.2M)的空间,而现在只有1MB的可用存储空间,那么究竟该作何处理呢?

    updated && correct:

       @yansha: 上述的位图方案,共需要扫描输入数据两次,具体执行步骤如下:

    • 第一次,只处理1—4999999之间的数据,这些数都是小于5000000的,对这些数进行位图排序,只需要约5000000/8=625000Byte,也就是0.625M,排序后输出。
    • 第二次,扫描输入文件时,只处理4999999-10000000的数据项,也只需要0.625M(可以使用第一次处理申请的内存)。
      因此,总共也只需要0.625M

    位图的的方法有必要强调一下,就是位图的适用范围为针对不重复的数据进行排序,若数据有重复,位图方案就不适用了。

        3、多路归并。诚然,在面对本题时,还可以通过计算分析出可以用如2的位图法解决,但实际上,很多的时候,我们都面临着这样一个问题,文件太大,无法一次性放入内存中计算处理,那这个时候咋办呢?分而治之,大而化小,也就是把整个大文件分为若干大小的几块,然后分别对每一块进行排序,最后完成整个过程的排序。k趟算法可以在kn的时间开销内和n/k的空间开销内完成对最多n个小于n的无重复正整数的排序。

        比如可分为2块(k=2,1趟反正占用的内存只有1.25/2M),1~4999999,和5000000~9999999。先遍历一趟,首先排序处理1~4999999之间的整数(用5000000/8=625000个字的存储空间来排序0~4999999之间的整数),然后再第二趟,对5000001~1000000之间的整数进行排序处理。在稍后的第二节、第三节、第四节,我们将详细阐述并实现这种多路归并排序磁盘文件的方案。
        4、读者思考。经过上述思路3的方案之后,现在有两个局部有序的数组了,那么要得到一个完整的排序的数组,接下来改怎么做呢?或者说,如果是K路归并,得到k个排序的子数组,把他们合并成一个完整的排序数组,如何优化?或者,我再问你一个问题,K路归并用败者树 和 胜者树 效率有什么差别?这些问题,请读者思考。

     

    第二节、多路归并算法的c++实现

        本节咱们暂抛开咱们的问题,阐述下有关多路归并算法的c++实现问题。在稍后的第三节,咱们再来具体针对咱们的磁盘文件排序问题阐述与实现。

        在了解多路归并算法之前,你还得了解归并排序的过程,因为下面的多路归并算法就是基于这个流程的。其实归并排序就是2路归并,而多路归并算法就是把2换成了k,即多(k)路归并。下面,举个例子来说明下此归并排序算法,如下图所示,我们对数组8 3 2 6 7 1 5 4进行归并排序:

        归并排序算法简要介绍:
    一、思路描述:
        设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中。
        
        二路归并排序的过程是:
        (1)把无序表中的每一个元素都看作是一个有序表,则有n个有序子表;
        (2)把n个有序子表按相邻位置分成若干对(若n为奇数,则最后一个子表单独作为一组),每对中的两个子表进行归并,归并后子表数减少一半;
        (3)反复进行这一过程,直到归并为一个有序表为止。

        二路归并排序过程的核心操作是将一维数组中相邻的两个有序表归并为一个有序表。

    二、分类:
        归并排序可分为:多路归并排序、两路归并排序 。
        若归并的有序表有两个,叫做二路归并。一般地,若归并的有序表有k个,则称为k路归并。二路归并最为简单和常用,既适用于内部排序,也适用于外部排序。本文着重讨论外部排序下的多(K)路归并算法。

    三、算法分析:
        1、稳定性:归并排序是一种稳定的排序。
        2、存储结构要求:可用顺序存储结构。也易于在链表上实现。
        3、时间复杂度: 对长度为n的文件,需进行lgn趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。。
        4、空间复杂度:需要一个辅助向量来暂存两有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。
           注意:若用单链表做存储结构,很容易给出就地的归并排序。
       
        总结:与快速排序相比,归并排序的最大特点是,它是一种稳定的排序方法。归并排序一般多用于外排序。但它在内排方面也占有重要地位,因为它是基于比较的时间复杂度为O(N*Log(N))的排序算法中唯一稳定的排序,所以在需要稳定内排序时通常会选择归并排序。归并排序不要求对序列可以很快地进行随机访问,所以在链表排序的实现中很受欢迎。

        好的,介绍完了归并排序后,回到咱们的问题。由第一节,我们已经知道,当数据量大到不适合在内存中排序时,可以利用多路归并算法对磁盘文件进行排序。

        我们以一个包含很多个整数的大文件为例,来说明多路归并的外排序算法基本思想。假设文件中整数个数为N(N是亿级的),整数之间用空格分开。首先分多次从该文件中读取M(十万级)个整数,每次将M个整数在内存中使用快速排序之后存入临时文件,然后使用多路归并将各个临时文件中的数据再次整体排好序后存入输出文件。显然,该排序算法需要对每个整数做2次磁盘读和2次磁盘写。以下是本程序的流程图:

        本程序是基于以上思想对包含大量整数文件的从小到大排序的一个简单实现,这里没有使用内存缓冲区,在归并时简单使用一个数组来存储每个临时文件的第一个元素。下面是多路归并排序算法的c++实现代码(在第四节,将给出多路归并算法的c实现): 

    //copyright@ 纯净的天空 && yansha  
    //5、July,updated,2010.05.28。  
    #include <iostream>  
    #include <ctime>  
    #include <fstream>  
    //#include "ExternSort.h"using namespace std;  
    //使用多路归并进行外排序的类  
    //ExternSort.h  
    /** 大数据量的排序* 多路归并排序* 以千万级整数从小到大排序为例* 一个比较简单的例子,没有建立内存缓冲区*/  
    #ifndef EXTERN_SORT_H  
    #define EXTERN_SORT_H  
    
    #include <cassert>class ExternSort  
    {  
    public:  
    	void sort()  
    	{  
    		time_t start = time(NULL);  
    		//将文件内容分块在内存中排序,并分别写入临时文件    
    		int file_count = memory_sort();  
    		//归并临时文件内容到输出文件  
    		merge_sort(file_count);  
    		time_t end = time(NULL);printf("total time:%f/n", (end - start) * 1000.0/ CLOCKS_PER_SEC);  
        }  
    	
        //input_file:输入文件名  
    	//out_file:输出文件名  
    	//count: 每次在内存中排序的整数个数  
        ExternSort(const char *input_file, const char * out_file, int count)  
        {  
    		m_count = count;  
    		m_in_file = new char[strlen(input_file) + 1];  
    		strcpy(m_in_file, input_file);  
    		m_out_file = new char[strlen(out_file) + 1];  
    		strcpy(m_out_file, out_file);  
        }  
        virtual ~ExternSort()  
    	{  
    		delete [] m_in_file;  
    		delete [] m_out_file;  
    	}  
    private:  
    	int m_count;   
    	//数组长度char *m_in_file;    
    	//输入文件的路径  
    	char *m_out_file;   
    	//输出文件的路径  
    protected:  
    	int read_data(FILE* f, int a[], int n)  
    	{  
    		int i = 0;  
    		while(i < n && (fscanf(f, "%d", &a[i]) != EOF))   
    			i++;  
    		printf("read:%d integer/n", i);  
    		return i;  
    	}  
    	void write_data(FILE* f, int a[], int n)  
    	{  
    		for(int i = 0; i < n; ++i)  
    			fprintf(f, "%d ", a[i]);  
    	}  
    	char* temp_filename(int index)  
    	{  
    		char *tempfile = new char[100];  
    		sprintf(tempfile, "temp%d.txt", index);  
    		return tempfile;  
    	}  
    	static int cmp_int(const void *a, const void *b)  
    	{  
    		return *(int*)a - *(int*)b;  
    	}  
    
    	int memory_sort()  
    	{  
    		FILE* fin = fopen(m_in_file, "rt");  
    		int n = 0, file_count = 0;int *array = new int[m_count];  
    		
    		//每读入m_count个整数就在内存中做一次排序,并写入临时文件  
    		while(( n = read_data(fin, array, m_count)) > 0)  
    		{  
    			qsort(array, n, sizeof(int), cmp_int);   //这里,调用了库函数阿,在第四节的c实现里,不再调qsort。    
    			char *fileName = temp_filename(file_count++);  
    			FILE *tempFile = fopen(fileName, "w");  
    			free(fileName);  
    			write_data(tempFile, array, n);  
    			fclose(tempFile);  
    		}  
    		delete [] array;  
    		fclose(fin);  
    		return file_count;  
    	}  
    	
    	void merge_sort(int file_count)  
    	{  
    		if(file_count <= 0)   
    			return;  
    		//归并临时文件FILE *fout = fopen(m_out_file, "wt");  
    		FILE* *farray = new FILE*[file_count];  
    		int i;  
    		for(i = 0; i < file_count; ++i)  
    		{  
    			char* fileName = temp_filename(i);  
    			farray[i] = fopen(fileName, "rt");  
    			free(fileName);  
    		}  
    		int *data = new int[file_count];  
    		//存储每个文件当前的一个数字  
    		bool *hasNext = new bool[file_count];  
    		//标记文件是否读完  
    		memset(data, 0, sizeof(int) * file_count);  
    		memset(hasNext, 1, sizeof(bool) * file_count);  
    		for(i = 0; i < file_count; ++i)  
    		{  
    			if(fscanf(farray[i], "%d", &data[i]) == EOF)  
    				//读每个文件的第一个数到data数组  
    				hasNext[i] = false;  
    		}  
    
    		while(true)  
    		{  
    			//求data中可用的最小的数字,并记录对应文件的索引  
    			int min = data[0];  
    			int j = 0;  
    			while (j < file_count && !hasNext[j])  
    				j++;  
    			if (j >= file_count)    
    				//没有可取的数字,终止归并  
    				break;  
    			for(i = j + 1; i < file_count; ++i)  
    			{  
    				if(hasNext[i] && min > data[i])  
    				{  
    					min = data[i];  
    					j = i;  
    				}  
    			}  
    			if(fscanf(farray[j], "%d", &data[j]) == EOF)   
    				//读取文件的下一个元素  
    				hasNext[j] = false;  
    			fprintf(fout, "%d ", min);  
    		}  
    
    		delete [] hasNext;  
    		delete [] data;  
    		for(i = 0; i < file_count; ++i)  
    		{  
    			fclose(farray[i]);  
    		}
    		delete [] farray;  
    		fclose(fout);  
    	}  
    };  
    #endif  
    
    //测试主函数文件  
    /** 大文件排序* 数据不能一次性全部装入内存* 排序文件里有多个整数,整数之间用空格隔开*/  
    
    const unsigned int count = 10000000;   
    // 文件里数据的行数const unsigned int number_to_sort = 1000000;   
    //在内存中一次排序的数量  
    const char *unsort_file = "unsort_data.txt";   
    //原始未排序的文件名  
    const char *sort_file = "sort_data.txt";   
    //已排序的文件名  
    void init_data(unsigned int num);   
    
    //随机生成数据文件  
    
    int main(int argc, char* *argv)  
    {  
    	srand(time(NULL));  
    	init_data(count);  
    	ExternSort extSort(unsort_file, sort_file, number_to_sort);  
    	extSort.sort();  
    	system("pause");  
    	return 0;  
    }  
    
    void init_data(unsigned int num)  
    {  
    	FILE* f = fopen(unsort_file, "wt");  
    	for(int i = 0; i < num; ++i)  
    		fprintf(f, "%d ", rand());  
    	fclose(f);  
    } 

    程序测试:读者可以继续用小文件小数据量进一步测试。

     

    第三节、磁盘文件排序的编程实现

        ok,接下来,我们来编程实现上述磁盘文件排序的问题,本程序由两部分构成:
    1、内存排序
    由于要求的可用内存为1MB,那么每次可以在内存中对250K的数据进行排序,然后将有序的数写入硬盘。
    那么10M的数据需要循环40次,最终产生40个有序的文件。
    2、归并排序

    1. 将每个文件最开始的数读入(由于有序,所以为该文件最小数),存放在一个大小为40的first_data数组中;
    2. 选择first_data数组中最小的数min_data,及其对应的文件索引index;
    3. 将first_data数组中最小的数写入文件result,然后更新数组first_data(根据index读取该文件下一个数代替min_data);
    4. 判断是否所有数据都读取完毕,否则返回2。

    所以,本程序按顺序分两步,第一步、Memory Sort,第二步、Merge Sort。程序的流程图,如下图所示(感谢F的绘制)。

    然后,编写的完整代码如下:

    //copyright@ yansha
    //July、updated,2011.05.28。
    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <time.h>
    using namespace std;
    
    int sort_num = 10000000;
    int memory_size = 250000;  
    
    //每次只对250k个小数据量进行排序
    int read_data(FILE *fp, int *space)
    {
    	int index = 0;
    	while (index < memory_size && fscanf(fp, "%d ", &space[index]) != EOF)
    		index++;
    	return index;
    }
    
    void write_data(FILE *fp, int *space, int num)
    {
    	int index = 0;
    	while (index < num)
    	{
    		fprintf(fp, "%d ", space[index]);
    		index++;
    	}
    }
    
    // check the file pointer whether valid or not.
    void check_fp(FILE *fp)
    {
    	if (fp == NULL)
    	{
    		cout << "The file pointer is invalid!" << endl;
    		exit(1);
    	}
    }
    
    int compare(const void *first_num, const void *second_num)
    {
    	return *(int *)first_num - *(int *)second_num;
    }
    
    string new_file_name(int n)
    {
    	char file_name[20];
    	sprintf(file_name, "data%d.txt", n);
    	return file_name;
    }
    
    int memory_sort()
    {
    	// open the target file.
    	FILE *fp_in_file = fopen("data.txt", "r");
    	check_fp(fp_in_file);
    	int counter = 0;
    	while (true)
    	{
    		// allocate space to store data read from file.
    		int *space = new int[memory_size];
    		int num = read_data(fp_in_file, space);
    		// the memory sort have finished if not numbers any more.
    		if (num == 0)
    			break;
    
    		// quick sort.
    		qsort(space, num, sizeof(int), compare);
    		// create a new auxiliary file name.
    		string file_name = new_file_name(++counter);
    		FILE *fp_aux_file = fopen(file_name.c_str(), "w");
    		check_fp(fp_aux_file);
    
    		// write the orderly numbers into auxiliary file.
    		write_data(fp_aux_file, space, num);
    		fclose(fp_aux_file);
    		delete []space;
    	}
    	fclose(fp_in_file);
    
    	// return the number of auxiliary files.
    	return counter;
    }
    
    void merge_sort(int file_num)
    {
    	if (file_num <= 0)
    		return;
    	// create a new file to store result.
    	FILE *fp_out_file = fopen("result.txt", "w");
    	check_fp(fp_out_file);
    
    	// allocate a array to store the file pointer.
    	FILE **fp_array = new FILE *[file_num];
    	int i;
    	for (i = 0; i < file_num; i++)
    	{
    		string file_name = new_file_name(i + 1);
    		fp_array[i] = fopen(file_name.c_str(), "r");
    		check_fp(fp_array[i]);
    	}
    
    	int *first_data = new int[file_num];   
    	//new出个大小为0.1亿/250k数组,由指针first_data指示数组首地址
    	bool *finish = new bool[file_num];
    	memset(finish, false, sizeof(bool) * file_num);
    
    	// read the first number of every auxiliary file.
    	for (i = 0; i < file_num; i++)
    		fscanf(fp_array[i], "%d ", &first_data[i]);
    	while (true)
    	{
    		int index = 0;
    		while (index < file_num && finish[index])
    			index++;
    
    		// the finish condition of the merge sort.
    		if (index >= file_num)
    			break;
    		//主要的修改在上面两行代码,就是merge sort结束条件。
    		//要保证所有文件都读完,必须使得finish[0]...finish[40]都为真
    		//July、yansha,555,2011.05.29。
    
    		int min_data = first_data[index];
    		// choose the relative minimum in the array of first_data.
    		for (i = index + 1; i < file_num; i++)
    		{
    			if (min_data > first_data[i] && !finish[i])   
    				//一旦发现比min_data更小的数据first_data[i]
    			{
    				min_data = first_data[i];    
    				//则置min_data<-first_data[i]index = i;                   
    				//把下标i 赋给index。
    			}
    		}
    
    		// write the orderly result to file.
    		fprintf(fp_out_file, "%d ", min_data);
    		if (fscanf(fp_array[index], "%d ", &first_data[index]) == EOF)
    			finish[index] = true;
    	}
    
    	fclose(fp_out_file);
    	delete []finish;
    	delete []first_data;
    	for (i = 0; i < file_num; i++)
    		fclose(fp_array[i]);
    	delete [] fp_array;
    }
    
    int main()
    {
    	clock_t start_memory_sort = clock();
    	int aux_file_num = memory_sort();
    	clock_t end_memory_sort = clock();
    	cout << "The time needs in memory sort: " << end_memory_sort - start_memory_sort << endl;
    	clock_t start_merge_sort = clock();
    	merge_sort(aux_file_num);
    	clock_t end_merge_sort = clock();
    	cout << "The time needs in merge sort: " << end_merge_sort - start_merge_sort << endl;
    	system("pause");
    	return 0;
    }

    其中,生成数据文件data.txt的代码在第一节已经给出。

    程序测试

        1、咱们对1000W数据进行测试,打开半天没看到数据,

        2、编译运行上述程序后,data文件先被分成40个小文件data[1....40],然后程序再对这40个小文件进行归并排序,排序结果最终生成在result文件中,自此result文件中便是由data文件的数据经排序后得到的数据。

        3、且,我们能看到,data[i],i=1...40的每个文件都是有序的,如下图:

        4、最终的运行结果,如下,单位统一为ms:

        由上观之,我们发现,第一节的位图方案的程序效率是最快的,约为14s,而采用上述的多路归并算法的程序运行时间约为25s。时间主要浪费在读写磁盘IO上,且程序中用的库函数qsort也耗费了不少时间。所以,总的来说,采取位图方案是最佳方案。

    小数据量测试:

        我们下面针对小数据量的文件再测试一次,针对20个小数据,每趟对4个数据进行排序,即5路归并,程序的排序结果如下图所示。

    运行时间:

    0ms,可以忽略不计了,毕竟是对20个数的小数据量进行排序:

    沙海拾贝:

        我们不在乎是否能把一个软件产品或一本书最终完成,我们更在乎的是,在完成这个产品或创作这本书的过程中,读者学到了什么,能学到什么?所以,不要一味的马上就想得到一道题目的正确答案,请跟着我们一起逐步走向山巅。

    第四节、多路归并算法的c实现

        本多路归并算法的c实现原理与上述c++实现一致,不同的地方体现在一些细节处理上,且对临时文件的排序,不再用系统提供的快排,即上面的qsort库函数,是采用的三数中值的快速排序(个数小于3用插入排序)的。而我们知道,纯正的归并排序其实就是比较排序,在归并过程中总是不断的比较,为了从两个数中挑小的归并到最终的序列中。ok,此程序的详情请看:

    //copyright@ 555
    //July、2011.05.29。
    #include <assert.h>
    #include <time.h> 
    #include <stdio.h>   
    #include <memory.h>
    #include <stdlib.h>
    
    void swap_int(int* a,int* b)
    {    
    	int c;    
    	c = *a;    
    	*a = *b;    
    	*b = c;
    }
    
    //插入排序
    void InsertionSort(int A[],int N)
    {    
    	int j,p;    
    	int tmp;   
    	for(p = 1; p < N; p++)    
    	{       
    		tmp = A[p];
    		for(j = p;j > 0 && A[j - 1] >tmp;j--)        
    		{            
    			A[j] = A[j - 1];        
    		}       
    		
    		A[j] = tmp;   
    	}
    }
    
    //三数取中分割法
    int Median3(int A[],int Left,int Right)
    {
    	int Center = (Left + Right) / 2;
    	if (A[Left] > A[Center])
    		swap_int(&A[Left],&A[Center]);
    	if (A[Left] > A[Right])
    		swap_int(&A[Left],&A[Right]);
    	if (A[Center] > A[Right])
    		swap_int(&A[Center],&A[Right]);
    	swap_int(&A[Center],&A[Right - 1]);
    	return A[Right - 1];
    }
    
    //快速排序
    void QuickSort(int A[],int Left,int Right)
    {
    	int i,j;
    	int Pivot;
    	const int Cutoff = 3;
    	if (Left + Cutoff <= Right)
    	{
    		Pivot = Median3(A,Left,Right);
    		i = Left;
    		j = Right - 1;
    		while (1)
    		{
    			while(A[++i] < Pivot){;}
    			while(A[--j] > Pivot){;}
    			if (i < j)
    				swap_int(&A[i],&A[j]);
    			else
    				break;
    		}
    		swap_int(&A[i],&A[Right - 1]); 
    		
    		QuickSort(A,Left,i - 1);
    		QuickSort(A,i + 1,Right);
    	}
    	else
    	{
    		InsertionSort(A+Left,Right - Left + 1);
    	}
    }
    
    //const int  KNUM  = 40;        
    //分块数
    const int  NUMBER = 10000000; 
    //输入文件最大读取的整数的个数
    //为了便于测试,我决定改成小文件小数据量进行测试。
    const int  KNUM  = 4;        
    //分块数const int  NUMBER = 100; 
    //输入文件最大读取的整数的个数
    const char *in_file = "infile.txt";
    const char *out_file = "outfile.txt";
    //#define OUTPUT_OUT_FILE_DATA
    //数据量大的时候,没必要把所有的数全部打印出来,所以可以把上面这句注释掉。
    void  gen_infile(int n)
    {
    	int i;
    	FILE *f = fopen(in_file, "wt"); 
    	for(i = 0;i < n; i++)
    		fprintf(f,"%d ",rand());
    	fclose(f);
    }
    
    int  read_data(FILE *f,int a[],int n)
    {
    	int i = 0;
    	while ((i < n) && (fscanf(f,"%d",&a[i]) != EOF))  
    		i++;
    	printf("read: %d integer/n",i);
    	return i;
    }
    
    void  write_data(FILE *f,int a[],int n)
    {
    	int i;for(i = 0; i< n;i++)
    		fprintf(f,"%d ",a[i]);
    }
    
    char* temp_filename(int index)
    {
    	char *tempfile = (char*) malloc(64*sizeof(char));
    	assert(tempfile);
    	sprintf(tempfile, "temp%d.txt", index);
    	return tempfile;
    }
    
    //K路串行读取
    void k_num_read(void)
    {
    	char* filename;
    	int i,cnt,*array;
    	FILE* fin;
    	FILE* tmpfile;
    	//计算knum,每路应读取的整数个数int n = NUMBER/KNUM;
    	if (n * KNUM < NUMBER)n++;
    
    	//建立存储分块读取的数据的数组
    	array = (int*)malloc(n * sizeof(int));assert(array);
    	//打开输入文件
    	fin = fopen(in_file,"rt");
    	i = 0;
    	
    	//分块循环读取数据,并写入硬盘上的临时文件
    	while ( (cnt = read_data(fin,array,n))>0)
    	{
    		//对每次读取的数据,先进行快速排序,然后写入硬盘上的临时文件
    		QuickSort(array,0,cnt - 1);
    		filename = temp_filename(i++);
    		tmpfile = fopen(filename,"w");
    		free(filename);
    		write_data(tmpfile,array,cnt);
    		fclose(tmpfile);
    	}
    	assert(i == KNUM);
    	//没有生成K路文件时进行诊断
    	//关闭输入文件句柄和临时存储数组
    	fclose(fin);
    	free(array);
    }
    
    //k路合并(败者树)
    void k_num_merge(void)
    {
    	FILE *fout;
    	FILE **farray;
    	char *filename;
    	int  *data;
    	char *hasNext;
    	int i,j,m,min;
    #ifdef OUTPUT_OUT_FILE_DATAint id;
    #endif
    	//打开输出文件
    	fout = fopen(out_file,"wt");
    	//打开各路临时分块文件
    	farray = (FILE**)malloc(KNUM*sizeof(FILE*));
    	assert(farray);
    	for(i = 0; i< KNUM;i++)
    	{
    		filename = temp_filename(i);
    		farray[i] = fopen(filename,"rt");
    		free(filename);
    	}
    	
    	//建立KNUM个元素的data,hasNext数组,存储K路文件的临时数组和读取结束状态
    	data = (int*)malloc(KNUM*sizeof(int));
    	assert(data);
    	hasNext = (char*)malloc(sizeof(char)*KNUM);
    	assert(hasNext);
    	memset(data, 0, sizeof(int) * KNUM);
    	memset(hasNext, 1, sizeof(char) * KNUM);
    	
    	//读K路文件先读取第一组数据,并对读取结束的各路文件设置不可再读状态
    	for(i = 0; i < KNUM; i++)
    	{
    		if(fscanf(farray[i], "%d", &data[i]) == EOF)
    		{
    			hasNext[i] = 0;
    		}
    	}
    	
    	//读取各路文件,利用败者树从小到大输出到输出文件
    #ifdef OUTPUT_OUT_FILE_DATAid = 0;
    #endif
    	
    	j  = 0;F_LOOP:
    	if (j < KNUM)    
    		//以下这段代码嵌套过深,日后应尽量避免此类问题。
    	{
    		while(1==1)
    		{
    			min = data[j];
    			m = j;
    			for(i = j+1; i < KNUM; i++)
    			{
    				if(hasNext[i] == 1  && min > data[i])
    				{
    					min = data[i];m = i;
    				}
    			}
    
    			if(fscanf(farray[m], "%d", &data[m]) == EOF) 
    			{
    				hasNext[m] = 0;
    			}
    			fprintf(fout, "%d ", min);
    #ifdef OUTPUT_OUT_FILE_DATAprintf("fout :%d  %d/n",++id,min);
    #endif
    			if (m == j && hasNext[m] == 0)
    			{
    				for (i = j+1; i < KNUM; i++)
    				{
    					if (hasNext[m] != hasNext[i])
    					{
    						m = i;
    						//第i个文件未读完,从第i个继续往下读
    						break;
    					}
    				}
    				if (m != j)
    				{
    					j = m;
    					goto F_LOOP;
    				}
    				break;
    			}
    		}
    	}
    	
    	//关闭分配的数据和数组    
    	free(hasNext);   
    	free(data);       
    	for(i = 0; i < KNUM; ++i)   
    	{        
    		fclose(farray[i]);   
    	}   
    	free(farray);    
    	fclose(fout);
    }
    
    int main()    
    {   
    	time_t start = time(NULL),end,start_read,end_read,start_merge,end_merge;
    	gen_infile(NUMBER);    
    	end = time(NULL);   
    	printf("gen_infile data time:%f/n", (end - start) * 1000.0/ CLOCKS_PER_SEC);
    	start_read = time(NULL);k_num_read();    
    	end_read = time(NULL);   
    	printf("k_num_read time:%f/n", (end_read - start_read) * 1000.0/ CLOCKS_PER_SEC);
    	start_merge = time(NULL);
    	k_num_merge();    
    	end_merge = time(NULL);    
    	printf("k_num_merge time:%f/n", (end_merge - start_merge) * 1000.0/ CLOCKS_PER_SEC);   
    	end = time(NULL);   
    	printf("total time:%f/n", (end - start) * 1000.0/ CLOCKS_PER_SEC);    
    	return 0;  
    }  

    程序测试:

    在此,我们先测试下对10000000个数据的文件进行40趟排序,然后再对100个数据的文件进行4趟排序(读者可进一步测试)。如弄几组小点的数据,输出ID和数据到屏幕,再看程序运行效果。

    1. 10个数, 4组
    2. 40个数, 5组
    3. 55个数, 6组
    4. 100个数, 7组

     

    (备注:1、以上所有各节的程序运行环境为windows xp + vc6.0 + e5200 cpu 2.5g主频,2、感谢5为本文程序所作的大量测试工作)

    全文总结:

    1、关于本章中位图和多路归并两种方案的时间复杂度及空间复杂度的比较,如下:

                  时间复杂度       空间复杂度
    位图         O(N)               0.625M
    多位归并   O(Nlogn)        1M   

    (多路归并,时间复杂度为O(k*n/k*logn/k ),严格来说,还要加上读写磁盘的时间,而此算法绝大部分时间也是浪费在这上面)

    2、bit-map

    适用范围:可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下
    基本原理及要点:使用bit数组来表示某些元素是否存在,比如8位电话号码
    扩展:bloom filter可以看做是对bit-map的扩展

    问题实例:
    1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
    8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。
    2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

    将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map。

    3、[外排序适用范围]大数据的排序,去重基本原理及要点:外排序的归并方法,置换选择败者树原理,最优归并树扩展。问题实例:1).有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。这个数据具有很明显的特点,词的大小为16个字节,但是内存只有1m做hash有些不够,所以可以用来排序。内存可以当输入缓冲区使用。 

    4、海量数据处理

        有关海量数据处理的方法或面试题可参考此文,十道海量数据处理面试题与十个方法大总结。日后,会逐步实现这十个处理海量数据的方法。同时,送给各位一句话,解决问题的关键在于熟悉一个算法,而不是某一个问题。熟悉了一个算法,便通了一片题目。

    本章完。

        updated:有一读者朋友针对本文写了一篇文章为,海量数据多路归并排序的c++实现(归并时利用了败者树),地址为:http://www.cnblogs.com/harryshayne/archive/2011/07/02/2096196.html。谢谢,欢迎参考。


        版权所有,本人对本blog内所有任何内容享有版权及著作权。网络转载,请以链接形式注明出处。
    展开全文
  • 使用Excel批量替换文件名的固定部分

    千次阅读 2019-07-12 18:14:31
    今天在替换开机动画时,遇到了批量替换文件名的固定部分的问题,相信大家都遇到过。如果你还没有很好的办法,可以试试下面的方式(请看解决步骤)。 分析问题 先看下236个图片文件的名字,分析“2019.06.06 海尔...
  • 1、中文名文件上传后保存在linux服务器上文件名会乱码,但是我们通过SSH直接对服务器上的一个文件进行重命名是可以使用中文的,而且显示出来是正确的,这说明服务器是可以支持中文的。2、而为什么上传的中文名文件...
  • perl hash value/keys 排序(多种方法)

    千次阅读 2012-02-29 19:13:25
    如果是ASCII码排序,则代码如下: foreach my $key ( sort { $hash{$a} cmp $hash{$b} } keys %hash ) {  my $value = $hash{$key}; # do something with ($key, $value) } ...
  • 文件名在FAT文件系统上的实现

    千次阅读 2010-04-27 19:56:00
    文件名在FAT文件系统上的实现******************************* 长文件名在FAT文件系统上的实现 ******************************* 2008/10/28 asdjf@163.com www.armecos.com 一般来说,嵌入式里使用DOS传统...
  • 批量修改文件名 批量重命名

    千次阅读 2014-04-19 15:35:04
    之前下了好多歌,当然这个跟code 没关系了 ,但也放上来分享一下。 这些歌都是几年下来累积的,当然可能所有的明明
  • 【计算机基础】02Windows7操作系统

    万次阅读 2020-05-05 16:41:48
    第2章 中文Windows 7操作系统 2.1 操作系统概述 2.1.1 操作系统的基本概念 计算机的大脑。告诉计算机做什么及如何做。 1. 操作系统的作用: 1)操作系统是计算机系统应用平台的组成部分 系统软件或应用软件...
  • 1、中文名文件上传后保存在Linux服务器上文件名会乱码,但是我们通过SSH直接对服务器上的一个文件进行重命名是可以使用中文的,而且显示出来是正确的,这说明服务器是可以支持中文的。 2、而为什么上传的中文名...
  • 2,3,4,5,6,7,8,9,10,11,12等,在电脑上面显示时,是按照递增的方式,可是当用C#来读取时,默认按照的顺序是1,10,11,12,2,20,21,3,4,5,6,7,8,9,如果对文件顺序要求很严格,这样明显符合...
  • 1. 在Windows系统中启动任务管理器可使用:Ctrl+Alt+Delete、Ctrl+Shift+Esc、右击任务栏空白处 2. UNIX操作系统是一种多用户分时操作系统,Linux操作系统最早是由芬兰黑客为尝试在因特尔X86架构上提供自由免费的...
  • 如何给10^7个数据量的磁盘文件排序

    千次阅读 2012-08-25 11:54:36
    输入:一个最多含有n个重复的正整数(也就是说可能含有少于n个重复正整数)的文件,其中每个数都小于等于n,且n=10^7。 输出:得到从小到大升序排列的包含所有输入的整数的列表。 条件:最多有大约1MB的内存...
  • windows常用命令行命令

    万次阅读 多人点赞 2017-08-31 11:16:21
    windows常用命令 打开"运行"对话框(Win+R),输入cmd,打开控制台命令窗口... 也可以通过cmd /c 命令和cmd /k 命令的方式来直接运行命令 注:/c表示执行完命令后关闭cmd窗口;/k表示执行完命令后保留cmd窗口 # ...
  • windows bat 批处理脚本编写指南

    千次阅读 2020-02-01 20:20:45
    本文由浅入深,比较系统地讲解了windows bat 批处理脚本编写指南,包括:windows bat批处理脚本概述,变量使用, 程序返回码及errorlevel使用,stdin、stdout、stderr,if判断与&、&&、||, for循环使用,函数goto...
  • windows XP系统内核文件分析精简系统很有好处黑客防线 当然, 精简系统是要付出代价的. 不要贪得无厌, 免得系统坏了修不好啊. Windows XP个别 System32 文件 System32 文件夹下个别要移除的文件 我们就要删除另外600...
  • Windows.h 常用API函数【转】

    万次阅读 多人点赞 2018-08-12 01:32:13
    如模仿鼠标右击,想一想,一次鼠标右击有哪几步,分别是鼠标右键下,鼠标右键松开,如果你下鼠标右键松开,那它是不是鼠标右击,不是的,直到你松开鼠标右键,才能算是一次完整的鼠标右击.鼠标右键下的消息...
  • Windows 系统错误代码

    万次阅读 2016-06-15 14:49:09
    1-函数不正确。 2-系统找到指定的文件。 3-系统找到指定的路径。 4-系统无法打开文件。 5-拒绝访问。 6-句柄无效。 7-存储控制块被损坏。 8-存储空间不足,无法处理此命令。 9-存储控制块地址无效。 10...
  • 用java模拟windows下的dir指令

    千次阅读 2017-02-13 20:20:57
    这个是小小的程序是利用java模拟windows下的dir指令,并且已经尽可能的实现的其中的很多的功能。
  • windows常用文件操作dos命令

    千次阅读 2019-07-15 12:50:12
    文章目录dir 命令文件、目录(创建、修改、删除、复制、移动)创建目录创建文件,查看、追加、修改文件内容删除文件删除目录修改、替换目录或文件名复制、移动文件、目录复制移动other常用、实用的DOS命令 windows dos...
  • perl 排序

    千次阅读 2016-10-08 16:55:51
    这里{ $hash{$a} cmp $hash{$b} or $a cmp $b } 就先value再key进行了2次sort哦,sort返回的结果是排序后的keys列表,然后这个列表再交给map进行计算,返回一个匿名hash列表。访问方法与前面的相同,偶就详叙...
  • windows下常用的cmd命令

    千次阅读 2016-08-11 17:46:46
    本文介绍windows系统下常见的cmd命令 说明:按照使用频率排序win+r----------1. control:控制面版 2. calc:启动计算器 3. msconfig:系统配置 4. mspaint:画图 5. snippingtool:截图工具,支持无规则截图 6. ...
  • 磁盘排序

    千次阅读 2016-06-05 14:27:24
    位图的的方法有必要强调一下,就是位图的适用范围为针对重复的数据进行排序,若数据有重复,位图方案就适用了。 3 、多路归并。诚然,在面对本题时,还可以通过计算分析出可以用如 2 的位图法解决,但实际上...
  • 数据结构之九大排序算法C++

    千次阅读 2016-01-17 18:16:09
    九大排序算法的实现,使用C++封装
  • Windows外壳名字空间的浏览

    千次阅读 2011-06-17 13:37:00
    Windows外壳名字空间的浏览 姜伟华 Windows95/98对Dos/Win3.x作了许多重大改进,在文件系统方面,它除了采用长文件名替代Dos中的8.3文件名以外,引入外壳名字空间(Shell Name Space)来代Dos文件
  • 对于fss2,fss10,fss1,fss11,fss02排序,如果想对数字识别大小排序,传统排序是字典排序能达到目标,如下表: 散列 字典排序 目标排序 fss2 fss1 fss1 fss10 fss02 fss2 fss1 ...
  • Windows标准控件

    千次阅读 2012-01-06 23:00:56
    使用Windows标准控件 我们在前面曾提到过,控件是一些行为标准化了的窗口,一般用于对话框或其它窗口中充当与用户交互的元素。在Visual C++中,可以使用的控件分成三类: (1) Windows标准控件 Windows标准控件...
  • Windows Shell编程

    千次阅读 2012-07-25 15:22:53
    第一章 Windows Shell是什么 一个操作系统外壳的不错的定义是它是一个系统提供的用户界面,它允许用户执行公共的任务,如访问文件系统,导出执行程序,改变系统设置等。MS-DOS有一个Command.COM扮演着这个角色。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,243
精华内容 11,297
关键字:

windows7按文件名排序不正确