CSV格式特殊字符轉義處理

  • CSV是逗號分隔值(comama separated value)的縮寫,也有一說是字符分隔值。因爲分隔符也可以是其他字符。
  • 當csv某個字段中包含換行(CRLF)、雙引號、逗號(分隔符)時,整個字段必須用雙引號括起來。例如:
"aaa","b CRLF
bb","ccc" CRLF
zzz,yyy,xxx
  • 當csv某個字段用雙引號括起來且字段中內容中還包含雙引號時,必須在該引號前面再添加一個雙引號進行轉義。
"aaa","b""bb","ccc"
// gcc main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define  CSV_DELIM                 ","
#define  CSV_QUOTE                 "\""
#define  CSV_QUOTE_CHAR            '"'
#define  CSV_CRLF                  "\r\n"
#define  CSV_SPECIAL_CHARS         ",\"\r\n"

// 注意:
// 1, 這裏假設escape buf足夠大,不會出現溢出問題  
// 2, 並未考慮原始字符串就已經被雙引號括起來的場景,如: "abc"這種可能已經不需要再加雙引號了   
int escape_csv_string(char* escape_buf, const char* raw_data)
{
    int    found_special_char = 0;                    // 標識是否發現了雙引號、逗號、CRLF中的任意一個特殊字符  
    const  char* first_non_quote_char = NULL;         // 記錄“後面第一個字符的位置
    const  char* special_chars = CSV_SPECIAL_CHARS;

    int    buf_used_len = 0;
    const  char* tmp_val = raw_data;
    while (*tmp_val)
    {
        if (!found_special_char)                      // 只要發現一個特殊字符,就需要在該字段前後加". 後面就不需要再查找特殊字符了  
        {
            for (const char* c = special_chars; *c; ++c)
            {
                if (*tmp_val == *c)
                {
                    // 發現特殊字符,首先在整個字符串前面加一個",並設置標誌位,以便整體遍歷結束後在尾部也追加一個"   
                    strcat(escape_buf, CSV_QUOTE);
                    ++buf_used_len;
                    found_special_char = 1;
                    break;
                }
            }
        }

        // 如果特殊字符是",直接追加寫進去兩個""
        if (*tmp_val == CSV_QUOTE_CHAR)
        {
            if (NULL != first_non_quote_char)
            {
                // 發現"後,首先將上一個"之後到這個"之前的部分拷貝到buf中
                strncpy(escape_buf + buf_used_len, first_non_quote_char, tmp_val - first_non_quote_char);
                buf_used_len += tmp_val - first_non_quote_char;
                escape_buf[++buf_used_len] = '\0';
            }

            buf_used_len += 2;
            strcat(escape_buf, CSV_QUOTE);
            strcat(escape_buf, CSV_QUOTE);
            first_non_quote_char = NULL;    
        }
        else
        {
            // 記錄第一個非"字符的位置
            if (NULL == first_non_quote_char)
                first_non_quote_char = tmp_val;
        }

        tmp_val++;
    }

    if (found_special_char)
    {
        if (NULL != first_non_quote_char)  // 最後一個字符是"
        {
            strcat(escape_buf, first_non_quote_char);
        }
        strcat(escape_buf, CSV_QUOTE);
    }
    else                                  // 說明字符串中沒有特殊字符
    {
        strcat(escape_buf, raw_data);
    }
    return 0;
}


int main(int argc, char* argv[])
{
    while (1)
    {
        printf("\nplsese input a string:\n");
        char input[1024] = {0};
        scanf("%s", input);
        char output[1024] = {0};
        escape_csv_string(output, input);
        printf("After escape: s\n", output);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章