c - 在 C 中解析 iCalendar 文件

标签 c regex parsing icalendar

我希望使用 C 来解析 iCalendar 文件。我有一个现有的结构设置和读取准备就绪,并且想逐行解析组件。

例如,我需要解析如下内容:

UID:uid1@example.com
DTSTAMP:19970714T170000Z
ORGANIZER;CN=John Doe;SENT-BY="mailto:smith@example.com":mailto:john.doe@example.com
CATEGORIES:Project Report, XYZ, Weekly Meeting
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party

以下是一些规则:

  • 每行的第一个词是属性名称
  • 属性名称后跟冒号 (:) 或分号 (;)
  • 如果是冒号那么属性值会直接在行尾内容的右边
  • 这里增加了一层复杂性,因为允许使用逗号分隔的值列表,然后将其存储在数组中。因此,例如 CATEGORIES 将在一个数组中包含 3 个元素作为值
  • 如果属性名后有分号,则后面有可选参数
  • 可选参数格式为ParamName=ParamValue。这里再次支持逗号分隔列表。
  • 可以有多个可选参数,如 ORGANIZER 行所示。只会有另一个分号,后跟下一个参数和值。
  • 另外一个麻烦事是,值中允许使用引号。如果某些内容在值的引号中,则需要将其视为值的一部分而不是语法的一部分。因此,引号中的分号并不意味着还有另一个参数,它是值的一部分。

我正在使用 strchr()strtok() 来解决这个问题,并从中获得了一些基本元素,但是它变得非常困惑和无组织,并且没有似乎是执行此操作的正确方法。

如何使用标准 C 库(或 POSIX 正则表达式库)实现如此复杂的解析器? (不是寻找完整的解决方案,只是起点)

最佳答案

这个答案是假设你想使用标准 C 推出你自己的解析器。在实践中,通常最好使用现有的解析器,因为他们已经考虑并处理了所有可能出现的奇怪事情。

我的高级方法是:

  • 读一行
  • 将指向此行开头的指针传递给函数 parse_line:
    • 在指针上使用 strcspn 来识别第一个 的位置:;(如果没有找到标记则中止)
    • 将文本保存为属性名
    • 当解析指针指向;时:
      • 调用函数 extract_name_value_pair 传递您的解析指针的地址。
      • 该函数将提取并保存名称和值,并将指针更新为指向条目后的 ;:。当然,此函数必须处理值中的引号以及它们可能是 ;: 的事实
    • (此时解析指针一直在:)
    • 将字符串的其余部分传递给函数 parse_csv,该函数将查找以逗号分隔的值(再次注意引号)并将找到的结果存储在正确的位置。

函数 parse_csvextract_name_value_pair 实际上应该首先开发和测试。制作一个测试套件并检查它们是否正常工作。然后编写您的整体解析器函数,根据需要调用这些函数。


此外,将所有内存分配代码编写为单独的函数。想一想您要将解析结果存储在什么数据结构中。然后编写该数据结构并对其进行测试,完全独立于解析代码。然后,编写解析代码并调用函数将结果数据插入数据结构。

真的不想将内存管理代码与解析代码混在一起。这使得调试变得更加困难。


当制作一个接受字符串的函数时(例如,上面所有三个命名函数,加上您认为需要的任何其他助手),您有几个关于它们接口(interface)的选项:

  • 接受指向空终止字符串的指针
  • 接受指向开始和结束的指针
  • 接受开始指针和整数长度

每种方法都有其优点和缺点:到处都写空终止符,然后在需要时取消写入,这很烦人;但是,当您想使用 strcspn 或其他字符串函数时,您收到的是一段已计算长度的字符串,这也很烦人。

此外,当函数需要让调用者知道它在解析中消耗了多少文本时,您有两个选择:

  • 接受指向字符的指针,返回消耗的字符数;调用函数会将两者相加以了解发生了什么
  • 接受指向字符指针的指针,并更新指向字符的指针。然后可以将返回值用作错误代码。

没有唯一的正确答案,随着经验的积累,您会更好地决定哪个选项会导致最干净的代码。

关于c - 在 C 中解析 iCalendar 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35024861/

相关文章:

css - Ruby:解析明文的结构化 block

c++ - 从 char 中删除转义字符

c - 将节点添加到树问题

C代码随机化

regex - 如果连续存在两个以上空格,请删除字符串中的空格

regex - 如何在 Postgresql 中去掉中文

python - 用于提取日期中月份和年份组合的正则表达式

parsing - 是否有关于 Erlang 解析转换的完整教程?

ruby - Ruby 中的语法解析

c++ - 如何测试中断延迟?