【问题标题】:Python parse filenames by custom field patternPython通过自定义字段模式解析文件名
【发布时间】:2013-02-20 09:09:11
【问题描述】:

本周开始学习python,所以我想我会用它而不是excel来解析文件路径中的一些字段。

我有大约 3000 个文件都符合命名约定。 /Household/LastName.FirstName.Account.Doctype.Date.extension

例如,这些文件之一可能被命名为:Cosby.Bill..Profile.2006.doc 完整路径是 /Volumes/HD/Organized Files/Cosby, Bill/Cosby.Bill..Profile.2006.doc

在这种情况下:

科斯比,比尔将成为家庭

家庭(Cosby,Bill)是实际文件的封闭文件夹

Bill 是名字

Cosby 将是姓氏

帐户字段被省略

Profile 是文档类型

2006 是日期

doc是扩展名

所有这些文件都位于此目录 /Volumes/HD/Organized Files/ 我使用终端和 ls 将所有文件的列表放入桌面上的 .txt 文件中,我正在尝试解析来自将文件路径分类为上面示例中的类别。理想情况下,我想输出到 csv,每个类别都有一列。这是我丑陋的代码:

def main():
    file = open('~/Desktop/client_docs.csv', "rb")
    output = open('~/Desktop/client_docs_parsed.txt', "wb")
    for line in file:
        i = line.find(find_nth(line, '/', 2))
        beghouse = line[i + len(find_nth(line, '/', 2)):]
        endhouse = beghouse.find('/')
        household = beghouse[:endhouse]
        lastn = (line[line.find(household):])[(line[line.find(household):]).find('/') + 1:(line[line.find(household):]).find('.')]
        firstn = line[line.find('.') + 1: line.find('.', line.find('.') + 1)]
        acct = line[line.find('{}.{}.'.format(lastn,firstn)) + len('{}.{}.'.format(lastn,firstn)):line.find('.',line.find('{}.{}.'.format(lastn,firstn)) + len('{}.{}.'.format(lastn,firstn)))]
        doctype_beg = line[line.find('{}.{}.{}.'.format(lastn, firstn, acct)) + len('{}.{}.{}.'.format(lastn, firstn, acct)):]
        doctype = doctype_beg[:doctype_beg.find('.')]
        date_beg = line[line.find('{}/{}.{}.{}.{}.'.format(household,lastn,firstn,acct,doctype)) + len('{}/{}.{}.{}.{}.'.format(household,lastn,firstn,acct,doctype)):]
        date = date_beg[:date_beg.find('.')]
        print '"',household, '"','"',lastn, '"','"',firstn, '"','"',acct, '"','"',doctype, '"','"',date,'"'

def find_nth(body, s_term, n):
    start = body[::-1].find(s_term)
    while start >= 0 and n > 1:
        start = body[::-1].find(s_term, start+len(s_term))
        n -= 1
    return ((body[::-1])[start:])[::-1]

if __name__ == "__main__": main()

它似乎工作正常,但是当有另一个封闭文件夹时我遇到了问题,然后它会移动我的所有字段......例如,而不是文件驻留在

/Volumes/HD/Organized Files/Cosby, Bill/

位于 /Volumes/HD/Organized Files/Resigned/Cosby, Bill/

我知道必须有一种不那么笨重的方法来解决这个问题。

【问题讨论】:

  • 你好。我在这里闻到了 XY 问题的味道 (meta.stackexchange.com/questions/66377/what-is-the-xy-problem) 你的目标是什么?获得正确的工具来实现您的目标是您真正的 (X) 问题。问这个问题是你试图达到你认为的解决方案的方式(Y)
  • 您可以使用os.path.basename() 获取路径的基本名称。然后使用str.split() 以句点分隔名称。
  • 布兰登,我昨天晚上发布了一个编辑。我刚刚还发布了第二个编辑。你那边有什么消息吗?
  • 刚刚评论了我的发现

标签: python regex parsing csv


【解决方案1】:

这是一个比你的函数更实用的工具find_nth() :
rstrip()

def find_nth(body, s_term, n):
    start = body[::-1].find(s_term)
    print '------------------------------------------------'
    print 'body[::-1]\n',body[::-1]
    print '\nstart == %s' % start
    while start >= 0 and n > 1:
        start = body[::-1].find(s_term, start+len(s_term))
        print 'n == %s    start == %s' % (n,start)
        n -= 1
    print '\n (body[::-1])[start:]\n',(body[::-1])[start:]
    print '\n((body[::-1])[start:])[::-1]\n',((body[::-1])[start:])[::-1]
    print '---------------\n'
    return ((body[::-1])[start:])[::-1]


def cool_find_nth(body, s_term, n):
    assert(len(s_term)==1)
    return body.rsplit(s_term,n)[0] + s_term


ss = 'One / Two / Three / Four / Five / Six / End'
print 'the string\n%s\n' % ss

print ('================================\n'
       "find_nth(ss, '/', 3)\n%s" % find_nth(ss, '/', 3) )

print '================================='
print "cool_find_nth(ss, '/', 3)\n%s" % cool_find_nth(ss, '/', 3)

结果

the string
One / Two / Three / Four / Five / Six / End

------------------------------------------------
body[::-1]
dnE / xiS / eviF / ruoF / eerhT / owT / enO

start == 4
n == 3    start == 10
n == 2    start == 17

 (body[::-1])[start:]
/ ruoF / eerhT / owT / enO

((body[::-1])[start:])[::-1]
One / Two / Three / Four /
---------------

================================
find_nth(ss, '/', 3)
One / Two / Three / Four /
=================================
cool_find_nth(ss, '/', 3)
One / Two / Three / Four /

编辑 1

这是另一个非常实用的工具:正则表达式

import re

reg = re.compile('/'
                 '([^/.]*?)/'
                 '([^/.]*?)\.'
                 '([^/.]*?)\.'
                 '([^/.]*?)\.'
                 '([^/.]*?)\.'
                 '([^/.]*?)\.'
                 '[^/.]+\Z')

def main():
    #file = open('~/Desktop/client_docs.csv', "rb")
    #output = open('~/Desktop/client_docs_parsed.txt', "wb")
    li = ['/Household/LastName.FirstName.Account.Doctype.Date.extension',
          '- /Volumes/HD/Organized Files/Cosby, Bill/Cosby.Bill..Profile.2006.doc']
    for line in li:
        print "line == %r" % line
        household,lastn,firstn,acct,doctype,date = reg.search(line).groups('')       
        print ('household == %r\n'
               'lastn     == %r\n'
               'firstn    == %r\n'
               'acct      == %r\n'
               'doctype   == %r\n'
               'date      == %r\n'
               % (household,lastn,firstn,acct,doctype,date))

if __name__ == "__main__": main() 

结果

line == '/Household/LastName.FirstName.Account.Doctype.Date.extension'
household == 'Household'
lastn     == 'LastName'
firstn    == 'FirstName'
acct      == 'Account'
doctype   == 'Doctype'
date      == 'Date'

line == '- /Volumes/HD/Organized Files/Cosby, Bill/Cosby.Bill..Profile.2006.doc'
household == 'Cosby, Bill'
lastn     == 'Cosby'
firstn    == 'Bill'
acct      == ''
doctype   == 'Profile'
date      == '2006'

编辑 2

我想知道当我发布最后一次编辑时我的大脑在哪里。以下内容也可以完成这项工作:

rig = re.compile('[/.]')
rig.split(line)[-7:-1] 

【讨论】:

  • 正则表达式解决方案符合我的要求。但是,当我尝试迭代我的文件时遇到错误。似乎没有找到模式,并且 groups() 正在获取 NoneType。以下是 txt 中一些文件的示例:
  • 你好,请问示例在哪里?
  • - /Volumes/HD/Organized Files/Cosby, Bill/Cosby.Bill..Profile.2006.doc
  • - /Volumes/HD/Organized Files/Cosby, Bill/Cosby.Bill..Profile.2006.doc 只有一个文件名吗?里面有两个点
  • 你在你的问题中写过:"文件格式为:/Household/LastName.FirstName.Account.Doctype.Date.extension" 我以为是您使用标识符 houshold、firstn、lastn 等的原因。但是如果格式是可变的,您必须精确地在文件名中捕获什么,然后对捕获的部分进行处理
【解决方案2】:

据我所知,我相信这将是一个解决方案,它不会依赖于以前编译的文件列表

import csv
import os, os.path

# Replace this with the directory where the household directories are stored.
directory = "home"
output = open("Output.csv", "wb")
csvf = csv.writer(output)

headerRow = ["Household", "Lastname", "Firstname", "Account", "Doctype", 
              "Date", "Extension"]

csvf.writerow(headerRow)

for root, households, files in os.walk(directory):
    for household in households:
        for filename in os.listdir(os.path.join(directory, household)):
            # This will create a record for each filename within the "household"
            # Then will split the filename out, using the "." as a delimiter
            # to get the detail
            csvf.writerow([household] + filename.split("."))
output.flush()
output.close()

这使用 os 库来“遍历”家庭列表。然后对于每个“家庭”,它将收集一个文件列表。它使用这个列表,在 csv 文件中生成记录,分解文件的名称,使用句点作为分隔符。

它利用 csv 库来生成输出,看起来有点像;

"Household,LastName,Firstname,Account,Doctype,Date,Extension"

如果不需要扩展,可以通过换行省略:

csvf.writerow([household] + filename.split("."))

csvf.writerow([household] + filename.split(".")[-1])

告诉它只用完文件名的最后一部分,然后从 headerRow 中删除“扩展”字符串。

希望这会有所帮助

【讨论】:

    【解决方案3】:

    有点不清楚问题是什么,但与此同时,这里有一些东西可以帮助您入门:

    #!/usr/bin/env python
    
    import os
    import csv
    
    with open("f1", "rb") as fin:
        reader = csv.reader(fin, delimiter='.')
        for row in reader:
            # split path
            row = list(os.path.split(row[0])) + row[1:]
            print ','.join(row)
    

    输出:

    /Household,LastName,FirstName,Account,Doctype,Date,extension
    

    另一种解释是您希望将每个字段存储在一个参数中 还有一条额外的路径把事情搞砸了……

    这是 row 在 for 循环中的样子:

    ['/Household/LastName', 'FirstName', 'Account', 'Doctype', 'Date', 'extension']
    

    那么解决方案可能是向后工作。

    row[-1] 分配给extension,将row[-2] 分配给date 等等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      • 2019-08-12
      相关资源
      最近更新 更多