用C解析iCalendar文件

时间:2016-01-26 22:00:12

标签: 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正则表达式库)实现这样一个复杂的解析器? (不是寻找整个解决方案,只是出发点)

1 个答案:

答案 0 :(得分:1)

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

我的高级方法是:

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

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

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

确实不希望内存管理代码与解析代码混淆。这使得它在指数上难以调试。

当创建一个接受字符串的函数(例如上面所有三个命名函数,以及你决定需要的任何其他帮助函数)时,你有一些关于它们的接口的选项:

  • 接受指向以null结尾的字符串
  • 的指针
  • 接受指向开始和一个接一个的结尾
  • 接受指向开始的指针和整数长度

每种方式都有它的优点和缺点:在任何地方写入null终结符然后在需要时再写入它是很烦人的;但是当你想使用strcspn或其他字符串函数但你收到一个长度计算的字符串时,它也很烦人。

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

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

没有一个正确的答案,根据经验,您可以更好地决定哪个选项可以获得最干净的代码。