根据文件名中嵌入的时间戳对文件进行排序、分组和处理

Sort, group and process files based on an embedded timestamp in the filename

如何按每个文件名中嵌入的日期字符串对这些文件进行排序?然后我想遍历同一天创建的所有文件。

我可以在 shell 中执行此操作,但速度很慢。我想在 python.

中做同样的事情

示例文件列表(总共有 2200 个文件)

  1. 泰勒·考恩谈阅读 202109200657.md
  2. 论诗202109210659.md
  3. Slava Akhmechet 谈集群阅读 202109200659.md
  4. 在 4X4 矩阵中构思 202109200717.md
  5. 绘图网格构思202109220830.md
  6. 听写201208251425.md

输出看起来像这样(用于最终使用 Plotly 绘图。)

20120825,1  
20210920,3  
20210921,1  
20210922,1  

我想按给定日期的文档计数排序,然后在文档计数中按日期排序。因此上面的结果 1、3 和 4 将按日期顺序列出:

20210920,3
20120825,1  
20210921,1  
20210922,1  

然后我想对每天的文档做其他事情,比如获取当天的总字数。

如果您尝试替换 shell 脚本,您的 Python 脚本可能需要执行以下操作。

  1. 列出目录的内容以获取文件名。
  2. 从文件名中提取日期(假设 \d{8} 的正则表达式模式匹配足以提取日期)。
  3. 按提取日期对文件进行排序或分组。
  4. 迭代这些组来做某事。
import pathlib
import re
from collections import defaultdict

date_pattern = re.compile(r"\d{8}")
target_dir = pathlib.Path("myfolder")

# Files is a dictionary mapping a date to the list of files with that date
files = defaultdict(list)
for child in target_dir.iterdir():
    # Skip directories
    if child.is_dir():
        continue
    match = date_pattern.search(child.name)
    # Skip files that do not match the date pattern
    if match is None:
        continue
    file_date = match.group()
    files[file_date].append(child)

for date, names in files.items():
    for filename in names:
        # Do something
        print(date, filename)

编辑:按日期排序

要按日期排序,可以修改最后一个代码块。

for date in sorted(files):
    for filename in files[date]:
        # Do something
        print(date, filename)

您也可以使用 for date, names in sorted(files.items(), key=lambda d: d[0]):

这是你需要的吗?下面的代码从每个文件中提取日期并将其附加到以日期为键的字典中,因此您的字典将采用以下格式:

{
date1: [list of files],
date2: [list of files]
}

代码如下:

from collections import defaultdict
import re
files = ['Tyler Cowen On Reading 202109200657.md',
'On Poems 202109210659.md',
'Slava Akhmechet On Reading In Clusters 202109200659.md',
'Ideation In A 4X4 Matrix 202109200717.md',
'Drawing Grid Ideation 202109220830.md',
'Dictation 201208251425.md']

out = defaultdict(list)
for file in files:
    date = re.search(r'.*\s(\d+).md', file)
    if date:
        date = date.group(1)[:8]
        out[date].append(file)
print (out)

输出:

defaultdict(<class 'list'>, {'20210920': ['Tyler Cowen On Reading 202109200657.md', 'Slava Akhmechet On Reading In Clusters 202109200659.md', 'Ideation In A 4X4 Matrix 202109200717.md'], '20210921': ['On Poems 202109210659.md'], '20210922': ['Drawing Grid Ideation 202109220830.md'], '20120825': ['Dictation 201208251425.md']})

请注意,此代码仅提供逻辑,并未获取目录中的文件列表,您只需创建所有必需文件的列表并在上面的代码中使用该列表。

这是我理解的任务列表。

  1. 从文件名中提取字符串时间戳--字符串和列表处理

  2. 将时间戳(包括小时、分钟、秒)规范化为日期戳(仅限年-月-日)以在一天内对文档进行分组——日期处理

  3. 按每天文档数降序排序,然后在每天文档数以内按日期升序排序--稳定排序

  4. 在同一日期对文档进行分组,以某种方式处理它们——将函数传递给其他函数

这涵盖了相当多的 Python 编程基础,所以我将在接下来的过程中进行解释。

我推荐使用 arrow 库来处理日期。 首先,安装箭头:

pip install arrow

import itertools
from collections import Counter
from pathlib import Path

import arrow

docs = [
    'Tyler Cowen On Reading 202109200657.md',
    'On Poems 202109210659.md',
    'Slava Akhmechet On Reading In Clusters 202109200659.md',
    'Ideation In A 4X4 Matrix 202109200717.md',
    'Drawing Grid Ideation 202109220830.md',
    'Dictation 201208251425.md',
]


def datestamp(filename):
    basename = Path(filename).stem
    date_as_string = basename.split()[-1]
    timestamp = arrow.get(date_as_string, 'YYYYMMDDhhmm')
    return timestamp.format('YYYYMMDD')

要从文件名中提取日期部分,您需要文档“基本名称”的最后一部分,位于最后一个 space.

之后

Python 的 .split() 方法在 whitespace(spaces,制表符等)处将字符串拆分为列表:

>>> basename = 'On Poems 202109210659'
>>> basename.split()
['On', 'Poems', '202109210659']

a_list[-1] 提取列表中的最后一项,因此:


>>> basename.split()[-1]
'202109210659'

获取文件基本名称中最后一个 space-- 时间戳之后的所有内容。

然后 arrow 用于将时间戳转换为日期戳,因此忽略文档时间并将同一天的文档组合在一起。

202109210659 -> 20210921

使用箭头而不是正则表达式意味着您可以处理文件名中的不同日期格式。

在这种情况下,将日期作为字符串排序是可行的,因为所有日期都是 YMD 格式,年份(最大的组成部分)后跟月份和日期。如果我们对 DMY 日期进行排序,我们会得到意想不到的(和不需要的)结果。

对于 Plotly 数据:

datestamps = [datestamp(doc) for doc in docs]
datestamps.sort()
docDates = Counter()
for date in datestamps:
    docDates[date] += 1
for date, doc_count in docDates.most_common():
    print(f'{date},{doc_count}')

Counter() 是来自 Python 标准库的一个有用的 class。 它的 .most_common() 方法用于首先对文档最多的日期进行排序:

>>> python docs.py
20210920,3
20120825,1
20210921,1
20210922,1

请注意,1-doc 日期是按日期进行二级排序的。 datestamps.sort()(在 .most_common() 之前)是按日期的二级排序,因为 Python 库排序函数是“稳定的”。迭代 Counter() 遵循原始插入顺序,因此 .most_common() 将在文档计数相同的情况下保留 datestamps.sort() 的原始日期顺序。

为了更好地理解稳定排序,see this answer。可能要走几步才能看懂。

要对共享给定日期的文档进行分组,首先按日期戳函数对文档进行排序,然后按相同的函数对它们进行分组。这使您可以处理与单个日期关联的所有文档(用于每日字数统计等)。为每个文件名计算日期戳“键”函数,然后用于在排序和分组时比较项目。

docs.sort(key=datestamp)
for date, docs_on_date in itertools.groupby(docs, key=datestamp):
    docs_today = list(docs_on_date)
    print(f'{date}: {docs_today}')
    # for doc in docs_today:
    #     # do_something_with(doc)

结果:

20120825: ['Dictation 201208251425.md']
20210920: ['Tyler Cowen On Reading 202109200657.md', 'Slava Akhmechet On Reading In Clusters 202109200659.md', 'Ideation In A 4X4 Matrix 202109200717.md']
20210921: ['On Poems 202109210659.md']
20210922: ['Drawing Grid Ideation 202109220830.md']