【问题标题】:Toolset to parse iCalendar file in C在 C 中解析 iCalendar 文件的工具集
【发布时间】:2016-01-29 01:00:34
【问题描述】:

我需要在 C 中解析一个 ics 文件,并将逐行进行。每条线的格式可能会有很大差异,但通常都有一个标准。

以下是我注意到的一些规则:

  • 有一个属性名称
  • 每个以分号开头的可选参数
    • 也可以有 CSV
    • 可以是双引号值,在这种情况下,逗号、分号和冒号等内容需要在此忽略
  • 冒号
  • 财产价值

这是一个需要解析的示例 ics 组件:

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

您会注意到在MAILTO 之类的内容中有一个:。只有第一个冒号会被解析,冒号后面的其余部分是属性值。

使用strtok() 之类的东西似乎基本足以解决这个问题。

应该使用正则表达式之类的东西来解决这个问题吗?研究一下,我看到了一个在 C# 中在 this stackoverflow answer 上完成的正则表达式解决方案示例。

【问题讨论】:

  • 不,不需要正则表达式。但是strtok() 对这类问题不太好。最好使用strchr()

标签: c regex


【解决方案1】:

你可以这样做

#include <stdlib.h>
#include <string.h>

int
main(void)
{
    FILE *ics;
    char line[100];

    ics = fopen("example.ics", "r");
    if (ics == NULL)
        return -1;
    while (fgets(line, sizeof(line), ics) != NULL)
    {
        char *separator;
        char *key;
        char *tail;
        char *value;

        if ((tail = strchr(line, '\n')) != NULL)
            *tail = '\0'; // Remove the trailing '\n'
        separator = strpbrk(line, ":;");
        if (separator == NULL)
            continue;
        *separator = '\0';

        key = line; // Maybe you want to strip surrounding white spaces
        value = separator + 1; // Maybe you want to strip surrounding white spaces

        fprintf(stdout, "%s --> %s\n", key, value);
    }
    fclose(ics);

    return 0;
}

为此使用正则表达式就像用火箭筒杀死苍蝇。

【讨论】:

  • 好吧,这看起来是一个不错的解决方案,但是您在我的示例中看到的不太基本的行(例如 ORGANIZER 行)呢?其中包含也需要解析的额外数据。
  • 嘿,你想看看我的新问题吗? stackoverflow.com/questions/35024861/… 。它解释并显示了更多这种解析可能需要更复杂的方式。你还会在更复杂的情况下只使用 strchr 吗?
  • @Fogest 改用strpbrk()
  • 谢谢,这看起来很有用。你如何建议使用这个函数,它看起来几乎和 strchr 一样?
  • 确切地说,创建一个结构来保存这些值。但是解析从名称中分离出来的值。然后,如果你有一个结构说Property,你可以property.name = strdup(key);property.value = isc_parse_value(value);。它比其他答案干净得多。
【解决方案2】:
// disclaimer : no support 
// code provided as a  example of minimal things one can do.

#include <malloc.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>

struct value { 
   struct value *next; 
   char *val; 
};
struct property { 
   struct property *next; 
   char *prop; 
};
struct parameter { 
   struct property *props; 
   struct value *vals; 
   struct parameter *next; 
   char *name; 
};
enum PARSE_STATE { PARAMETER, PROPERTY, VALUE };

//format for lines is...
//   PARAMETER[;PARAM_PROPERTY..]:VALUE[,VALUE2..]\n

struct parameter *parse( char *input )
{
    size_t start, end;
    char *buf;
    enum PARSE_STATE state;
    struct parameter *root = NULL;
    struct parameter *new_parameter;
    struct property *new_property;
    struct value *new_value;
    char in_quote = 0;
    start = end = 0;
    state = PARAMETER;
    while( input[end] )
    {
        switch( state ) 
        { 
        case PARAMETER : 
            if( input[end] == ';' || input[end] == ':' ) {
               new_parameter = malloc( sizeof( struct parameter ) );
               new_parameter->next = root;
               new_parameter->name = malloc( end - start + 1 );
               strncpy( new_parameter->name, input + start, end - start );
               new_parameter->name[end-start] = 0;
               new_parameter->props = new_parameter->vals = NULL;
               root = new_parameter;
               start = end + 1;
               if( input[end] == ';' )
                  state = PROPERTY;
               else 
                  state = VALUE;
             }
             break;
        case PROPERTY :
             if( input[end] == '"' ) {
                if( !in_quote ) 
                   in_quote = input[start];
                else if( input[start] == in_quote ) 
                   in_quote = 0;
                break;
             }
             if( in_quote ) break;
             if( input[end] == ';' || input[end] == ':' ) {
                new_property = malloc( sizeof( struct property ) );
                new_property->prop = malloc( end - start + 1 );
                strncpy( new_property->prop, input + start, end - start );
                new_property->prop[end-start] = 0;
                new_property->next = root->props;
                root->props = new_property;
                if( input[end] == ':' ) 
                   state = VALUE;
                start = end + 1;
                break;
             }
             break;   
        case VALUE : 
             if( input[end] == '\n' || input[end] == ',' ) {
                new_value = malloc( sizeof( struct value ) );
                new_value->val = malloc( end - start + 1 );
                strncpy( new_value->val, input + start, end - start );
                new_value->val[end-start] = 0;
                new_value->next = root->vals;
                root->vals = new_value;
                if( input[end] == '\n' ) 
                    state = PARAMETER;
                start = end + 1;
             }
             break;
        }
        end++;
    }
    if( end != start )
       fprintf( stderr, "missing newline at end of input\n" );
    return root;
}


void DumpResult( struct parameter *root )
{
   struct property *prop;
   struct value *val;
   for( ; root; root = root->next ) {
         printf( "%s ", root->name );
         for( prop = root->props; prop; prop = prop->next ) 
              printf( "; %s ", prop->prop );
         for( val = root->vals; val; val = val->next ) {
            if( val == root->vals ) 
               printf( " : %s ", val->val );
            else 
               printf( ", %s ", val->val );
         }
         printf( "\n" );
   }
}

并且...使用上面的代码。 这些值都被颠倒了......

void main( void )
{
    char *string = "UID:uid1@example.com\n"
                           "DTSTAMP:19970714T170000Z\n"
                           "ORGANIZER;CN=John Doe;SENT-BY=\"mailto:smith@example.com\":mailto:john.doe@example.com\n"
                           "CATEGORIES:Project Report, XYZ, Weekly Meeting\n"
                           "DTSTART:19970714T170000Z\n"
                           "DTEND:19970715T035959Z\n"
                           "SUMMARY:Bastille Day Party\n";
    struct parameter *thing = parse( string );
    DumpResult( thing );
}

【讨论】:

  • 不错的解决方案,糟糕的编码风格。理解代码所需的时间比需要的要多。考虑避免使用单行 if 语句并更好地使用空格来改进编码风格。另外,考虑使用int main(void),你知道区别吗?
  • void main() 会是一个更好的建议:) 无论哪种方式都没有使用任何参数...你知道吗?扩展了几个 If 语句;抱歉,玩围棋的坏习惯。
  • void main() 不是有效的签名。
猜你喜欢
  • 1970-01-01
  • 2011-03-25
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 2023-03-24
  • 2011-05-17
相关资源
最近更新 更多