如何用C中的引号分隔CSV来解析CSV?

时间:2011-02-15 21:06:38

标签: c linux parsing gcc

考虑一下,这条消息:

N,8545,01/02/2011 09:15:01.815,"RASTA OPTSTK 24FEB2011 1,150.00 CE",S,8.80,250,0.00,0

这只是一个样本。这个想法是,这是csv文件中的一行。现在,如果我要把它分成逗号,那么1150数字就会出现问题。

双引号内的字符串是可变长度的,但可以确定为一个“元素”(如果我可以使用该术语) 其他元素是由

分隔的元素

我该如何解析它? (除了Ragel解析引擎)

Soham

4 个答案:

答案 0 :(得分:4)

将字符串分成以逗号提供分隔的字段,表示逗号未嵌入带引号的字符串中。

快速执行此操作的方法是使用状态机。

boolean inQuote = false;
StringBuffer buffer= new StringBuffer();
// readchar() is to be implemented however you read a char
while ((char = readchar()) != -1) {
  switch (char) {

    case ',':
      if (inQuote == false) {
         // store the field in our parsedLine object for later processing.
         parsedLine.addField(buffer.toString());
         buffer.setLength(0);
      }
      break;

    case '"': 
      inQuote = !inQuote;
      // fall through to next target is deliberate.

    default:
      buffer.append(char);

  }
}

请注意,尽管这提供了一个示例,但还有一些需要考虑的CSV文件(如引号中的嵌入式引号,或者是否适合在示例中去除外部引号)。

答案 1 :(得分:1)

如果您不想添加外部库,快速而又脏的解决方案是将双引号转换为\ 0(字符串标记的结尾),然后使用sscanf分别解析三个字符串。丑陋但应该工作。

假设输入格式正确(否则您将不得不添加错误处理):

for (i=0; str[i]; i++)
  if (str[i] == '"') str[i] = 0;
str += sscanf(str, "%c,%d,%d/%d/%d %d:%d:%d.%d,", &var1, &var2, ..., &var9);
var10 = str; // it may be str+1, I don't remember if sscanf consumes also the \0
sscanf(str+strlen(var10), ",%c,%f,%d,%f,%d", &var11, &var12, ..., &var15);

如果您想立即免费var10,您显然必须复制str

答案 2 :(得分:0)

这是一个函数,用于从作为FILE *提供的输入文件中获取下一个CSV字段。它希望文件以文本模式打开,并支持带有嵌入式引号和换行符的带引号的字段。超过提供的缓冲区大小的字段将被截断。

int get_csv_field(FILE *f, char *buf, size_t size)
{
    char *p = buf;
    int c;
    enum { QS_UNQUOTED, QS_QUOTED, QS_GOTQUOTE } quotestate = QS_UNQUOTED;

    if (size < 1)
        return EOF;

    while ((c = getc(f)) != EOF)
    {
        if ((c == '\n' || c == ',') && quotestate != QS_QUOTED)
            break;

        if (c == '"')
        {
            if (quotestate == QS_UNQUOTED)
            {
                quotestate = QS_QUOTED;
                continue;
            }

            if (quotestate == QS_QUOTED)
            {
                quotestate = QS_GOTQUOTE;
                continue;
            }

            if (quotestate == QS_GOTQUOTE)
            {
                quotestate = QS_QUOTED;
            }
        }

        if (quotestate == QS_GOTQUOTE)
        {
            quotestate = QS_UNQUOTED;
        }

        if (size > 1)
        {
            *p++ = c;
            size--;
        }
    }

    *p = '\0';

    return c;
}

答案 3 :(得分:0)

我们自己libcsvRobert Gamble怎么样?