关于python 3.x:异国情调的CSV方言解析

Exotic CSV Dialect Parsing

我似乎找不到正确的python3 csv阅读器参数来解析这个特定的csv方言。生成csv的对象的行为如下:

分析器信息:

  • 引号字符:"(x22)
  • 字段分隔符:^(x5e)
  • 记录分隔符:(x0A)
  • 转义符(x5c)

生成此格式的csv如何工作:

  • 如果在字段中找到指定的记录分隔符,则引号字段
  • 如果在字段中找到指定的字段分隔符,请引用该字段。
  • 如果在字段中找到指定的引号字符,请引用该字段并转义引号字符
  • 如果在字段中找到指定的转义符,则不执行任何操作…

^最后一点是导致我出现问题的原因,因为特定行的第一个字段以反斜杠结尾。这会导致python3 csv解析器将第一个字段分隔符解释为转义。

见下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(xcve) ttucker@plato:~/tmp/csv$ python --version
Python 3.6.4
(xcve) ttucker@plato:~/tmp/csv$ cat test_csv.py
import csv
with open('exotic_dialect.csv') as f:
    data = f.readlines()
reader = csv.reader(data, delimiter='^', quotechar='"',
                    escapechar='\', quoting=csv.QUOTE_MINIMAL)
for row in reader:
    print(row)

(xcve) ttucker@plato:~/tmp/csv$ cat exotic_dialect.csv
a^b^c
a|^b^c
"a""^b^c
"a^"^b^c
a\^b^c
(xcve) ttucker@plato:~/tmp/csv$ python test_csv.py
['a', 'b', 'c']
['a|', 'b', 'c']
['a"', 'b', 'c']
['a^', 'b', 'c']
['a^b', 'c']

^ This last list should have three fields; i.e., ['a\', 'b', 'c']

所以,我的问题是:

  • 这个csv方言能被默认的python lib解析吗(但是有一些特定的选项我似乎找不到)
  • 这能被一些Python代码很容易地解析吗(同样,假设第一个字段以每个可打印的ASCII结尾)

  • 因此,首先对数据进行字符串替换不是最好的主意,因为没有任何方法可以确定是否应该在不了解上下文的情况下对未捕获的转义符进行转义。我最后只写了自己的解析器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    class XcCsv(object):
        def __init__(self, field_delim, record_delim, quote_char, escape_char):
            self._field_delim = field_delim
            self._record_delim = record_delim
            self._quote_char = quote_char
            self._escape_char = escape_char

            self._records = []
            self._record_buf = []
            self._field_buf =""
            self._in_quote = False
            self._in_escape = False

        # This could be different ...
        def _walker(self, data):
            data_length = len(data)
            if data_length == 1:
                self._parse_char(data)
            else:
                for d in data:
                    self._walker(d)

        def _parse_char(self, char):
            if self._in_escape:
                self._field_buf += char
                self._in_escape = False
            elif char == self._escape_char:
                if self._in_quote:
                    self._in_escape = True
                else:
                    self._field_buf += char
            elif char == self._quote_char:
                if self._in_quote:
                    if self._in_escape == True:
                        self._field_buf += char
                    else:
                        self._in_quote = False
                else:
                    self._in_quote = True
            elif char == self._field_delim:
                if self._in_quote:
                    self._field_buf += char
                else:
                    self._record_buf.append(self._field_buf)
                    self._field_buf =""
            elif char == self._record_delim:
                if self._in_quote:
                    self._field_buf += char
                else:
                    self._record_buf.append(self._field_buf)
                    self._records.append(self._record_buf)
                self._record_buf = []
                self._field_buf =""
            else:
                self._field_buf += char

        def reader(self, data):
            self._walker(data)
            for rec in self._records:
                print(rec)

    csv = XcCsv("^","
    ","'","\")
    data = open("exotic_dialect.csv").readlines()
    csv.reader(data)

    我不是这方面的专家,但是

    1)可能不会。您希望相同的转义符在此上下文中执行两个不同的操作,这将无法区分。这些csv解析器通常首先处理转义字符。

    csv解析器如何确定此示例的正确行为?

    • fo\^o^bar
      • ["fo\","o","bar"]
      • ["fo^o","bar"]

    2)我个人会通过一些预处理来运行您的csv,这样您就可以正确地解析您的文件(用\\^替换\^)