Как сгруппировать файлы на основе одинаково расположенных символов / шаблонов в Python? - PullRequest
0 голосов
/ 23 января 2019

У меня есть набор имен файлов в списке в разных папках, как показано ниже:

Входные файлы

['ABC.dat',
'ABC10.dat',
'ABC956.dat',
'ABC_DEF_1.dat',
'ABC_DEF_2.dat',
'ABC_DEF_3.dat',
'ABC10_DEF_1.dat',
'ABC10_DEF_2.dat',
'ABC10_DEF_3.dat',
'ABC956_DEF_1.dat',
'ABC956_DEF_2.dat',
'ABC956_DEF_3.dat',
'XYZ_ABC_1.dat',
'XYZ_ABC_2.dat',
'XYZ10_ABC_1.dat',
'XYZ10_ABC_2.dat',
'XYZ956_ABC_1.dat',
'XYZ956_ABC_2.dat',
'XYZ_PQR_JKL.dat',
'XYZ_PQR_JKL_1.dat',
'XYZ_PQR10_JKL.dat',
'XYZ_PQR10_JKL_1.dat',
'XYZ_PQR956_JKL.dat',
'XYZ_PQR956_JKL_1.dat'] 

Я хочу сгруппировать файлыследующим образом:

Список вывода

[['ABC.dat', 'ABC10.dat', 'ABC956.dat'],
['ABC_DEF_1.dat', 'ABC10_DEF_1.dat.dat', 'ABC956_DEF_1.dat'],
['ABC_DEF_2.dat', 'ABC10_DEF_2.dat.dat', 'ABC956_DEF_2.dat'],
['ABC_DEF_3.dat', 'ABC10_DEF_3.dat.dat', 'ABC956_DEF_3.dat'],
['XYZ_ABC_1.dat', 'XYZ10_ABC_1.dat', 'XYZ956_ABC_1.dat'],
['XYZ_ABC_2.dat', 'XYZ10_ABC_2.dat', 'XYZ956_ABC_2.dat'],
['XYZ_PQR_JKL.dat', 'XYZ_PQR10_JKL.dat', 'XYZ_PQR956_JKL.dat'],
['XYZ_PQR_JKL_1.dat', 'XYZ_PQR10_JKL_1.dat', 'XYZ_PQR956_JKL_1.dat']]

То есть файлы должны быть сгруппированы на основе структуры файлов.Примечание. DEF_1 и DEF_2 должны быть сгруппированы отдельно.Числа 10, 956 являются случайными, то есть они не известны заранее.Ниже приводится MWE, который группируется на основе первых нескольких букв, полученных из OP , как я могу распространить его на другие буквы, которые являются DEF.

MWE

import os
import random
import errno
import itertools
from itertools import repeat

#--------------------------------------
# Main rename code
for root, dirs, files in os.walk('./input_folder'):
    for dir in dirs: 
        print (dir)
        output_files = [s for s in os.listdir(os.path.join(root,dir)) if s.endswith('.dat')]
        groups =  [list(g) for _, g in itertools.groupby(sorted(output_files), lambda x: x[0:2])]    # obtained from Aaron's answer https://gis.stackexchange.com/a/206053
        print (groups)

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Вы, вероятно, должны попробовать Regular Expression in Python (re library).

re.findall (pattern, string, flags = 0)
Вернуть все неперекрывающиеся совпадения шаблона в строке в виде списка строк.

# suppose files is a string holds all your file names (you could join your file names together)
files = """ABC 956.dat
ABC DEF 10.dat
ABC DEF 23.dat
ABC DEF 27.dat
ABC DEF 54.dat
XYZ-ABC 158.dat
XYZ-ABC 221.dat
XYZ-ABC 668.dat
XYZ-ABC 919.dat"""

# use re to find the names with certain pattern.
import re
g1 = re.findall('ABC \d{3}.dat', files)
# ['ABC 956.dat', 'ABC 158.dat', 'ABC 221.dat', 'ABC 668.dat', 'ABC 919.dat']
g2 = re.findall('ABC DEF \d{2}.dat', files)
# ['ABC DEF 10.dat', 'ABC DEF 23.dat', 'ABC DEF 27.dat', 'ABC DEF 54.dat']

# more groups to go with similar settings

В примере кода \ d представляет одну цифру, а {n} представляет номер вхождения предыдущего шаблона. Таким образом, \ d {3} означает 3 цифры.

Вы можете получить больше информации о регулярном выражении здесь .

0 голосов
/ 24 января 2019

Это основано на ответе Ajax1234.Это позволяет избежать лишних вычислений этого ответа.Вместо того, чтобы делать рекурсивное разбиение с помощью отношения эквивалентности.Это делает дискриминацию.Это снижает стоимость с N**2/2 звонков до is_match до всего N звонков до key.key использует None в качестве подстановочного знака для частей имени файла, которые являются цифрами.

import re
from collections import defaultdict

def key(v):
    return tuple(None if p.isdigit() else p for p in re.sub('.dat$', '', v).split())

def partition(l, key=None):
    d = defaultdict(list)
    for e in l:
        k = key(e) if key is not None else e
        d[k].append(e)
    return [d[k] for k in sorted(d)]

partition(filter(None, _input.split('\n')), key=key)

Это приводит к:

[['ABC 956.dat', 'ABC 114.dat', 'ABC 577.dat', 'ABC 782.dat'],
 ['ABC 127 JKL.dat', 'ABC 272 JKL.dat', 'ABC 462 JKL.dat', 'ABC 707 JKL.dat'],
 ['ABC 258 PQR0 0.dat', 'ABC 551 PQR0 3.dat', 'ABC 606 PQR0 5.dat', 'ABC 654 PQR0 2.dat'],
 ['ABC 542 PQR1 4.dat', 'ABC 234 PQR1 2.dat', 'ABC 432 PQR1 7.dat', 'ABC 766 PQR1 5.dat'],
 ['ABC 137 XYZ 97.dat', 'ABC 164 XYZ 25.dat', 'ABC 418 XYZ 13.dat', 'ABC 913 XYZ 11.dat'],
 ['ABC DEF 10.dat', 'ABC DEF 23.dat', 'ABC DEF 27.dat', 'ABC DEF 54.dat'],
 ['XYZ-ABC 158.dat', 'XYZ-ABC 221.dat', 'XYZ-ABC 668.dat', 'XYZ-ABC 919.dat']]

Кажется, я не был 'Достаточно ясно, где внести изменения:

def key(v):
    return tuple(None if p.isdigit() else p for p in re.sub('.dat$', '', v).split('_'))

partition(filter(None, input_list), key=key)
0 голосов
/ 23 января 2019

Вы можете использовать рекурсию:

import re
def is_match(a, b):
  a, b = re.sub('\s\w+\.dat$', '', a).split(), re.sub('\s\w+\.dat$', '', b).split()
  if len(a) != len(b):
     return False
  return all(c == d if not c.isdigit() and not d.isdigit() else c.isdigit() and d.isdigit() for c, d in zip(a, b))

def group_vals(d, _current = []):
  if _current:
    yield _current
  if d:
    _start, *_d = d
    yield from group_vals([i for i in _d if not is_match(_start, i)], [_start, *[i for i in _d if is_match(_start, i)]])

files = list(filter(None, _input.split('\n')))
print(list(group_vals(files)))

Вывод:

[['ABC 956.dat', 'ABC 114.dat', 'ABC 577.dat', 'ABC 782.dat'], 
 ['ABC DEF 10.dat', 'ABC DEF 23.dat', 'ABC DEF 27.dat', 'ABC DEF 54.dat'], 
  ['XYZ-ABC 158.dat', 'XYZ-ABC 221.dat', 'XYZ-ABC 668.dat', 'XYZ-ABC 919.dat'], 
  ['ABC 127 JKL.dat', 'ABC 272 JKL.dat', 'ABC 462 JKL.dat', 'ABC 707 JKL.dat'], 
  ['ABC 137 XYZ 97.dat', 'ABC 164 XYZ 25.dat', 'ABC 418 XYZ 13.dat', 'ABC 913 XYZ 11.dat'], 
  ['ABC 258 PQR0 0.dat', 'ABC 551 PQR0 3.dat', 'ABC 606 PQR0 5.dat', 'ABC 654 PQR0 2.dat'], 
  ['ABC 542 PQR1 4.dat', 'ABC 234 PQR1 2.dat', 'ABC 432 PQR1 7.dat', 'ABC 766 PQR1 5.dat']]
...