Python: сравнить имена файлов в папке

Имя _файла1: Data_A_2015-07-29_16-25-55-313.txt

filename2: Data_B_2015-07-29_16-25-55-313.txt

Мне нужно сравнить все файлы в папке, чтобы убедиться, что для каждой метки времени есть ОДИН A и ОДИН B файл.

Вторая и центсекундная части имени файла не всегда одинаковы в обоих файлах, так что я считаю Date_%H:%M -> 2 файлов за каждую минуту, это то, что я ищу

(например: Data_A_2015-07-29_16-25-55-313.txt а также Data_B_2015-07-29_16-25-54-200.txt принадлежат друг другу)

Я попробовал следующий код:

for root,dirs,files in os.walk(source):
for a_items in files:
    if a_items.__contains__("A"):
        A_ALL_List.append(a_items)         # One List with all A Files
        a_1 = a_item.split('_')[1]           # A Part
        a_2 = a_item.split('_',)[2]          # Date Part
        a_3 = a_item.split('_')[3]           # TimeStamp %H%M%S%SS incl. .txt
        a_4 = a_3.rsplit('.txt',1)[0]        # TimeStamp %H%N%S%SS excl. .txt
        a_5 = a_4.rsplit ('-',1)[0]          # TimeStamp %H%M%S
        a_6 = a_5.rsplit ('-',1)[0]          # TimeStamp %H%M
        a_lvl1 = a_1 + '_' + a_2 +'_' + a_3  # A_Date_FULLTimeStamp.txt
        A_Lvl1.append(a_lvl1)                # A_Date_TimeStamp.txt LIST
        a_lvl2 = a_lvl1.rsplit('.txt',1)[0]  # Split .txt
        A_Lvl2.append(a_lvl2)                # A_Date_TimeStamp LIST
        a_lvl3 = a_1 + '_' + a_2 + '_' + a_5 # A_Date_(%H%M%S)TimeStamp
        A_Lvl3.append(a_lvl3)                # A_Date_(%H%M%S)TimeStamp LIST
        a_lvl4 = a_2 + '_' + a_4             # Date_FULLTimeStamp
        A_Lvl4.append(a_lvl4)                # Date_FULLTimeStamp LIST
        a_lvl5 = a_2 + '_' + a_5             # Date_(%H%M%S)TimeStamp
        A_Lvl5.append(a_lvl5)                # Date_(%H%M%S)TimeStamp LIST
        a_lvl6 = a_2 + '_' + a_6             # Date_(%H%M)TimeStamp
        A_Lvl6.append(a_lvl6)                # Date_(%H%M)TimeStamp LIST
for b_items in files:                        # Did the same for B now
    if b_items.__contains__("B"):
        B_All_List.append(b_items)

Таким образом, я получил списки для обоих имен файлов, содержащих только те части, которые я хочу сравнить -> например, если бы я сравнил списки A_Lvl6 с B_Lvl6, я бы сравнил только часть даты, а также часы и минуты из отметки времени.

Я понял, что файлов B больше, чем файлов A, поэтому я перешел:

for Difference in B_Lvl6: # Data in B
if Difference not in A_Lvl6: # Not in A
    DiffList.append(Difference)

Таким образом, я получил вывод данных, где у меня не было A-файлов, а B-файлов -> DiffList

Теперь я хотел бы найти соответствующие B-файлы из этого DiffList (так как не найдено подходящих A-файлов) и переместить эти B-файлы в другую папку -> В основной папке должны быть только A и B-файлы с соответствующими метками времени (%H). % М)

Мой вопрос (наконец-то):

  1. Как мне управлять последней частью, где я хочу избавиться от всех файлов A или B без партнера TimeStamp.

  2. Является ли мой метод правильным способом решения такой проблемы или это полностью безумие? Я использую Python уже 1,5 недели, поэтому любые предложения по пакетам и учебникам будут приветствоваться.

Решение, которое я использовал:

source='/tmp'

import os
import re`
import datetime as dt

pat=re.compile(r'^Data_(A|B)_(\d{4}-\d{2}-\d{2}_\d+-\d+-\d+-\d+)')
def test_list(l):
return (len(l)==2 and {t[1] for t in l}!=set('AB'))   

def round_time(dto, round_to=60):
     seconds = (dto - dto.min).seconds
     rounding = (seconds-round_to/2) // round_to * round_to
     return dto + dt.timedelta(0,rounding-seconds,-dto.microsecond)             

fnames={}
for fn in os.listdir(source):
    p=os.path.join(source, fn)
    if os.path.isfile(p):
         m=pat.search(fn)
         if m:   
           d=round_time(dt.datetime.strptime(m.group(2), '%Y-%m-%d_%H-%M-%S-%f'), round_to=60)
           fnames.setdefault(str(d), []).append((p, m.group(1)))

for k, v in [(k, v) for k, v in fnames.items() if not test_list(v)]:
   for fn in v:
      print fn[0]

3 ответа

Решение

Учитывая эти пять имен файлов:

$ ls Data*
Data_A_2015-07-29_16-25-55-313.txt  
Data_B_2015-07-29_16-25-54-200.txt
Data_A_2015-07-29_16-26-56-314.txt  
Data_B_2015-07-29_16-26-54-201.txt
Data_A_2015-07-29_16-27-54-201.txt

Вы можете использовать регулярное выражение, чтобы найти ключевую информацию: Демо

Поскольку мы имеем дело с метками времени, время должно быть округлено до ближайшего интересующего нас времени.

Вот функция, которая округляется вверх или вниз до ближайшей минуты:

import datetime as dt

def round_time(dto, round_to=60):
    seconds = (dto - dto.min).seconds
    rounding = (seconds+round_to/2) // round_to * round_to
    return dto + dt.timedelta(0,rounding-seconds,-dto.microsecond)  

Объедините это с циклическим просмотром файлов, вы можете объединить их в словарь списков, ключом которого будет отметка времени, округленная до минуты.

(Я подозреваю, что все ваши файлы находятся в одном каталоге, поэтому я показываю это с помощью os.listdir вместо os.walk, поскольку os.walk рекурсивно проходит через несколько каталогов)

import os
import re
import datetime as dt

pat=re.compile(r'^Data_(A|B)_(\d{4}-\d{2}-\d{2}_\d+-\d+-\d+-\d+)')

fnames={}
for fn in os.listdir(source):
    p=os.path.join(source, fn)
    if os.path.isfile(p):
        m=pat.search(fn)
        if m:   
            d=round_time(dt.datetime.strptime(m.group(2), '%Y-%m-%d_%H-%M-%S-%f'), round_to=60)
            fnames.setdefault(str(d), []).append((p, m.group(1)))

print fnames

Печать:

{'2015-07-29 16:28:00': [('/tmp/Data_A_2015-07-29_16-27-54-201.txt', 'A')], '2015-07-29 16:27:00': [('/tmp/Data_A_2015-07-29_16-26-56-314.txt', 'A'), ('/tmp/Data_B_2015-07-29_16-26-54-201.txt', 'B')], '2015-07-29 16:26:00': [('/tmp/Data_A_2015-07-29_16-25-55-313.txt', 'A'), ('/tmp/Data_B_2015-07-29_16-25-54-200.txt', 'B')]}

Пять файлов имеют один файл, который не имеет пары. Вы можете отфильтровать для всех списков файлов, которые не имеют длину два или не совпадают пары A и B.

Сначала определите тестовую функцию, которая будет проверять это:

def test_list(l):
    return (len(l)==2 and {t[1] for t in l}==set('AB'))   

Затем используйте понимание списка, чтобы найти все записи из dict, которые не соответствуют вашим условиям:

>>> [(k, v) for k, v in fnames.items() if not test_list(v)]
[('2015-07-29 16:28:00', [('/tmp/Data_A_2015-07-29_16-27-54-201.txt', 'A')])]

Затем действуйте на эти файлы:

for k, v in [(k, v) for k, v in fnames.items() if not test_list(v)]:
    for fn in v:
        print fn  # could be os.remove(fn) 

Тот же основной метод работает с os.walk но у вас могут быть файлы в нескольких каталогах.

Вот полный список:

source='/tmp'

import os
import re
import datetime as dt

pat=re.compile(r'^Data_(A|B)_(\d{4}-\d{2}-\d{2}_\d+-\d+-\d+-\d+)')

def test_list(l):
    return (len(l)==2 and {t[1] for t in l}==set('AB'))   

def round_time(dto, round_to=60):
    seconds = (dto - dto.min).seconds
    rounding = (seconds+round_to/2) // round_to * round_to
    return dto + dt.timedelta(0,rounding-seconds,-dto.microsecond)             

fnames={}
for fn in os.listdir(source):
    p=os.path.join(source, fn)
    if os.path.isfile(p):
        m=pat.search(fn)
        if m:   
            d=round_time(dt.datetime.strptime(m.group(2), '%Y-%m-%d_%H-%M-%S-%f'), round_to=60)
            fnames.setdefault(str(d), []).append((p, m.group(1)))

for k, v in [(k, v) for k, v in fnames.items() if not test_list(v)]:
    for fn in v:
        print fn[0]    # This is the file that does NOT have a pair -- delete?

Я думаю, что просто игнорировать вторую и миллисекундную часть не очень хорошая идея. Может случиться, что у одного из ваших файлов будет 01:01:59:999, а у другого - 01: 02: 00: 000. Разница составляет всего одну миллисекунду, но она влияет и на мельчайшую часть. Лучшим решением будет анализ даты и времени и вычисление времени между ними. Но давайте перейдем к простой глупой версии. Я думал, что-то подобное может сделать работу. Приспособьте это к своим потребностям, если это не совсем то, что вам нужно:

import os
import re

pattern = re.compile(r'^Data_(?P<filetype>A|B)_(?P<datetime>\d\d\d\d\-\d\d\-\d\d_\d\d\-\d\d)\-\d\d\-\d\d\d\.txt$')

def diff_dir(dir, files):
    a_set, b_set = {}, {}
    sets  = {'A': a_set, 'B': b_set}
    for file in files:
        path = os.path.join(dir, file)
        match = pattern.match(file)
        if match:
            sets[match.group('filetype')][match.group('datetime')] = path
        else:
            print("Filename doesn't match our pattern: " + path)
    a_datetime_set, b_datetime_set = set(a_set.keys()), set(b_set.keys())
    a_only_datetimes = a_datetime_set - b_datetime_set
    b_only_datetimes = b_datetime_set - a_datetime_set
    for dt in a_only_datetimes:
        print(a_set[dt])
    for dt in b_only_datetimes:
        print(b_set[dt])

def diff_dir_recursively(rootdir):
    for dir, subdirs, files in os.walk(rootdir):
        diff_dir(dir, files)

if __name__ == '__main__':
    # use your root directory here
    rootdir = os.path.join(os.path.dirname(__file__), 'dir')
    diff_dir_recursively(rootdir)

Я хотел опубликовать частичный ответ, чтобы указать, как можно назначить результаты разделения именам и дать им значимые имена. Обычно это немного облегчает решение проблемы.

def match_files(files):
    result = {}

    for filename in files:
        data, letter, date, time_txt = filename.split('_')
        time, ext = time_txt.split('.')
        hour, min, sec, ns = time.split('-')

        key = date + '_' + hour + '-' + min

        # Initialize dictionary if it doesn't already exist.
        if not result.has_key(key):
            result[key] = {}

        result[key][letter] = filename


    return result



filename1 = 'Data_A_2015-07-29_16-25-55-313.txt'
filename2 = 'Data_B_2015-07-29_16-25-55-313.txt'

file_list = [filename1, filename2]


match_files(file_list)

Выход:

In [135]: match_files(file_list)
Out[135]: 
{'2015-07-29_16-25': {'A': 'Data_A_2015-07-29_16-25-55-313.txt',
  'B': 'Data_B_2015-07-29_16-25-55-313.txt'}}
Другие вопросы по тегам