文件名匹配 - 字符串的中间位置

时间:2014-07-21 20:22:21

标签: python

我的目录中的文件格式如下:LnLnnnnLnnn.txt

其中L =字母,n =数字。例如:p2c0789c001.txt

我想根据第二个数字(即0789)是奇数还是偶数来分隔这些文件。

如果第二个数字的范围介于0001-0009之间,我只能设法使用此代码:

odd_files = []
for root, dirs, filenames in os.walk('.'):
   for filename in fnmatch.filter(filenames, 'p2c000[13579]*.txt'):
       odd_files.append(os.path.join(root, filename))

这将返回文件:['./p2c0001c054.txt', './p2c0003c055.txt', './p2c0005c056.txt', './p2c0007c057.txt', './p2c0009c058.txt']

任何建议我如何才能让任何给定的四位数字工作?

4 个答案:

答案 0 :(得分:1)

会这样做吗?

import re
regex = re.compile("[a-z][0-9][a-z]([0-9]{4})[a-z][0-9]{3}.txt")
filter(lambda x: int(regex.match(x).groups()[0]) % 2 == 1, fnmatch)

答案 1 :(得分:1)

如果它有点毛茸茸的话,你可以随时把它变成发电机并手工编写测试代码:

def odd_files_generator():
    for root, dirs, filenames in os.walk('.'):
        for filename in filenames:
            if filename[6] in '13579':
                yield filename

odd_files = list(odd_files_generator)

如果您的测试变得非常难以表达,请将if filename ...行替换为您明确的测试代码。

答案 2 :(得分:1)

最简单的解决方案是扩展您的通配符以匹配更多内容。

为此,我可能会做类似的事情:

for filename in fnmatch.filter(filenames, '??????[13579]*.txt'):

这将匹配您的值之前的任何字符,它将匹配您的通配符类中的任何奇数值,然后它将接受任何匹配的事后。

这有点严重,因为它是aaaaaaa3alkjfdhalkjfshglkjzsdhfgs.txt匹配,这是超级粗略。如果您知道您正在行走的目录中的数据得到很好的控制,那可能就行了。更好的解决方案可能是更多地指定一些东西。这可以通过以下表达式完成:

'[a-z][0-0][a-z][0-9][0-9][0-9][13579][a-z][0-9][0-9][0-9].txt'

使用Unix样式通配符的fnmatch.filter方法。这意味着您可以使用以下内容:

? - 匹配任何单个字符 * - 从无到有,无所不能 [] - 这匹配一类东西,使用 - 为范围和!排除

答案 3 :(得分:1)

构建这种过滤器没有特别的魔力。它只是 需要仔细构建适当的正则表达式和测试 反对。当使用具有大量重复组件的复杂模式时, 错误很容易蔓延。我喜欢定义辅助功能 如果需要,规范更易于人工阅读并且更容易修改。

import re
import os

# helper functions for legible re construction
LETTER = lambda n='': '({0}{1})'.format('[A-Za-z]', n)
NUM    = lambda n='': '({0}{1})'.format('\d', n)

FILENAME = LETTER() + NUM() + LETTER() + NUM('{4}') + LETTER() + NUM('{3}') + '\.txt'
FILENAME_RE = re.compile(FILENAME)

is_odd = lambda n: int(n) % 2 > 0


def odd_nnnn(f):
    """
    Determine if the given filename `f` matches our desired LnLnnnnLnnn.txt pattern
    with the second group of numbers (nnnn) odd.
    """
    m = FILENAME_RE.search(f)
    return m is not None and is_odd(m.group(4))


if __name__ == '__main__':
    print "Search pattern:", FILENAME

    files = ['./p2c0001c054.txt', './p2c0001c055.txt', './p2c0003c055.txt', './p2c0005c056.txt', './p2c0022c056.txt', './p2c0004c056.txt', './p2c0007c057.txt', './p2c0009c058.txt', './p2c8888c056.txt', ]
    files = [ os.path.normpath(f) for f in files ]

    root = '/users/test/whatever'

    odd_paths = [ os.path.join(root, f) for f in files if odd_nnnn(f) ]

    print odd_paths

唯一真正的缺点是,它更加冗长,特别是与 Brad Beattie 等超紧凑的答案相比。


[更新]后来我发现定义正则表达式的更简洁方法可能是:

FILENAME = "LnL(nnnn)Lnnn\.txt"

FILENAME_PAT = FILENAME.replace('L', r'[A-Za-z]').replace('n', r'\d')
FILENAME_RE = re.compile(FILENAME_PAT)

这更接近原始的  LnLnnnLnnn.txt'描述。匹配表达式必须从m.group(4)更改为m.group(1),因为只有一个组以这种方式捕获。