You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHP中用preg_replace过滤ICS文件,保留指定行并移除过期事件?

问题描述

我正在用PHP和JS开发一款应用,为避免代码冗余及过多依赖外部库,没采用Google API,而是通过ICS文件实现会议时间冲突检测功能,目前已自行编写JS解析器用于提取事件的起止时间。

当前存在以下问题:

  • 包含10年事件记录的完整ICS文件传输给客户端,不仅导致代码冗余(多达15000行),还存在日程隐私泄露风险
  • 需要过滤ICS文件,仅保留BEGIN:VEVENT、END:VEVENT以及以DTSTART、DTEND开头的行;同时希望能移除已结束的事件,只保留DTEND时间在2023年及以后的事件

ICS文件片段示例

BEGIN:VEVENT
DTSTART:20200907T170000Z
DTEND:20200907T180000Z
DTSTAMP:20221231T224008Z
UID:(redacted)
CREATED:20200820T161956Z
DESCRIPTION:
LAST-MODIFIED:20200820T162009Z
LOCATION:
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:Contact (redacted name here)
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20201101
DTEND;VALUE=DATE:20201104
DTSTAMP:20221231T224008Z
UID:(redacted)
CREATED:20200818T153428Z
DESCRIPTION:
LAST-MODIFIED:20200818T153428Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:..REDACTED PRIVATE INFO
TRANSP:TRANSPARENT
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT4H0M0S
END:VALARM
END:VEVENT
BEGIN:VEVENT

期望处理后结果示例

BEGIN:VEVENT
DTSTART:20200907T170000Z
DTEND:20200907T180000Z
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20201101
DTEND;VALUE=DATE:20201104
END:VEVENT
BEGIN:VEVENT...
PHP 实现代码

下面是分两步处理的PHP代码,先过滤每个VEVENT块内的无关行,再移除已过期的事件:

<?php
// 读取原始ICS文件内容
$icsContent = file_get_contents('path/to/your/calendar.ics');

// 第一步:过滤每个VEVENT块,仅保留BEGIN:VEVENT、END:VEVENT、DTSTART、DTEND相关行
$filtered = preg_replace_callback(
    '/BEGIN:VEVENT(.*?)END:VEVENT/s',
    function($matches) {
        $eventLines = preg_split('/\r?\n/', $matches[1]);
        $keepLines = [];
        foreach ($eventLines as $line) {
            $trimmed = trim($line);
            if (empty($trimmed)) continue;
            // 低版本PHP替换为 substr($trimmed, 0, 7) === 'DTSTART' || substr($trimmed, 0, 5) === 'DTEND'
            if (str_starts_with($trimmed, 'DTSTART') || str_starts_with($trimmed, 'DTEND')) {
                $keepLines[] = $trimmed;
            }
        }
        return "BEGIN:VEVENT\n" . implode("\n", $keepLines) . "\nEND:VEVENT";
    },
    $icsContent
);

// 第二步:移除DTEND早于2023年的VEVENT块
$finalFiltered = preg_replace_callback(
    '/BEGIN:VEVENT(.*?)END:VEVENT/s',
    function($matches) {
        $eventContent = $matches[1];
        if (preg_match('/DTEND(?:;[^:]+)?:(\d{4})/', $eventContent, $dtendMatches)) {
            $year = (int)$dtendMatches[1];
            if ($year < 2023) {
                return '';
            }
        }
        return $matches[0];
    },
    $filtered
);

// 清理多余空行
$finalFiltered = preg_replace('/\n{2,}/', "\n", $finalFiltered);

// 输出或保存处理后的内容
echo $finalFiltered;
// file_put_contents('path/to/filtered-calendar.ics', $finalFiltered);
?>
代码说明
  1. 第一步过滤无关行:

    • 用preg_replace_callback匹配每个完整的VEVENT块(s修饰符支持跨行匹配)
    • 拆分块内每行,仅保留DTSTART、DTEND开头的行,重新组装成标准VEVENT结构
    • 自动忽略空行和VALARM等子块内容
  2. 第二步过滤过期事件:

    • 再次遍历每个VEVENT块,正则提取DTEND中的年份(兼容DTEND;VALUE=DATE这类带参数的格式)
    • 年份小于2023则移除整个事件块
  3. 清理空行:避免多次处理后出现连续空行,保证ICS格式整洁

注意事项
  • 若PHP版本低于8.0,将str_starts_with替换为substr($trimmed, 0, 7) === 'DTSTART'和substr($trimmed, 0, 5) === 'DTEND'
  • 代码针对标准ICS格式编写,若你的ICS存在多行折叠格式,需先展开折叠行再执行过滤

内容的提问来源于stack exchange,提问作者Shawn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:10:34