简单的CSV词法分析器

9 投票
1 回答
936 浏览
提问于 2025-04-18 18:32

我想用 pygments 按列给 CSV 文件上色,就像这里的图片一样:

enter image description here

可以看到,同一列的颜色是一样的。

目前 pygments 并没有包含 CSV 的解析器,因为 CSV 被认为是一种比较冷门的格式。所以我尝试自己写一个简单的解析器。以下是我尝试的代码:

tokens = {
    'root': [
        (r'^[^,\n]+', Name.Function), # first column
        (',', Comment),               # separator
        (r'[^,\n]+', Name.Decorator), # second column
        (',', Comment),               # separator
        (r'[^,\n]+', Name.Constant),  # third column
        (',', Comment),               # separator
    ],
}

但是它没有给任何列上色,只给第一列上了色:

enter image description here

据我所知,pygments 是通过逐个匹配正则表达式来工作的:当当前的正则表达式不匹配时,它会继续下一个,然后再从头开始。如果没有任何匹配,它会报错并向前移动一个字符(并把那个字符放在红框里)。对于像嵌套注释这样的复杂情况,会有状态管理,但我觉得对于 CSV 来说,一个状态就足够了。

然后我尝试了:

tokens = {
    'root': [
        (',', Comment),                           # separator
        (r'^[^,\n]+', Name.Function),             # first column
        (r'(?:^[^,\n]+)[^,\n]+', Name.Decorator), # second column
    ],
}

但是它把所有列都涂成了第二列的颜色:

enter image description here

这是一个示例数据:

account_id,parent_account_id,name,status
,A001,English,active
A001,,Humanities,active
A003,A001,,active
A004,A002,Spanish,

在 Emacs 中,我成功地得到了我想要的效果,使用了:

(add-hook 'csv-mode-hook
             (lambda ()
               "colors first 8 csv columns differently"
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),"
                                              1 'font-lock-function-name-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\)"
                                              2 'font-lock-variable-name-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\)"
                                              3 'font-lock-keyword-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\)"
                                              4 'font-lock-type-face)))
))

(其实我添加了超过 4 列,但这并不重要)

最终效果是:

enter image description here

1 个回答

8

哦,我用状态解决了这个问题:

tokens = {
    'root': [
        (r'^[^,\n]*', Name.Function, 'second'),
    ],
    'second': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Decorator), 'third'),
    ],
    'third': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Constant), 'fourth'),
    ],
    'fourth': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Variable), 'fifth'),
    ],
    'fifth': [
        (r'(,)([^,\n]*)', bygroups(Comment, Keyword.Type), 'unsupported'),
    ],
    'unsupported': [
        (r'.+', Comment),
        ],
}

它把前5列CSV的颜色设置得不一样,其他的列则标记为注释:

在这里输入图片描述

撰写回答