首页 > 解决方案 > 有没有办法从 CSV 文件中查找和设置 DataType,而无需事先指定?

问题描述

我注意到从 CSV 文件读取数据中的 user647772 的一个示例...

data = """True,foo,1,2.3,baz
False,bar,7,9.8,qux"""
reader = csv.reader(StringIO.StringIO(data), delimiter=",")
parsed = (({'True':True}.get(row[0], False),
       row[1],
       int(row[2]),
       float(row[3]),
       row[4])
      for row in reader)
 getBackData = list(parsed)

我找不到太多与为“已解析”变量编写的代码相关的 CSV 阅读器库。如果有人能指出我在 python 网站上的正确文档,我将不胜感激。

另外,我很想知道是否有一种方法可以在运行时确定数据类型并设置已解析变量的值(上图)。那么,如果我修改上面的代码,以下实现是否“有效”:

parsed = "((row[0], int(row[1]), int(row[2]), int(row[3]), row[4], row[5]) for row in rawReader)"

getBackData = list(eval((parsed)))

或者有什么更好的方法吗?

标签: pythoncsvparsingtypes

解决方案


parsed变量并不是很特别csv.reader(没有 CSV Reader 库,csv是 Python 标准库中的一个模块)。此语法是 Python generator expression. 尽管此代码有效,但在尝试说明一个概念时,将多个概念压缩到一个语句中并不总是最好的。

此代码的更适合初学者的形式可能如下所示:

reader = csv.reader(StringIO.StringIO(data), delimiter=",")

# use a conventional for loop to build up the getBackData list
getBackData = []
for row in reader:
    converted_row = (
        row[0] == 'True',
        row[1],
        int(row[2]),
        float(row[3]),
        row[4],
        )
    getBackData.append(converted_row)

更简洁的方法是将所有这些转换器推入一个convert_row函数,然后建立getBackData一个列表理解:

def convert_row(raw):
    return (
        raw[0] == 'True',
        raw[1],
        int(raw[2]),
        float(raw[3]),
        raw[4],
        )

reader = csv.reader(StringIO.StringIO(data), delimiter=",")
getBackData = [convert_row(row) for row in reader]

然后你可以随心所欲地修改convert_row函数,但readergetBackData构造保持不变。

编辑:获取类型(没有很好的测试,但这是想法)

def try_bool(s):
    # will convert strings "True" and "False" to bools,
    # and raise an exception otherwise
    try:
        return {"True": True, "False": False}[s]
    except KeyError:
        raise ValueError("{!r} is not a valid bool".format(s))

def get_column_types(raw):
    types = []
    for col in raw:
        for test_type in (int, float, try_bool, str):
            try:
                test_type(col)
            except ValueError:
                # fail! not data of this type
                pass
            else:
                # it worked! add test_type to list of converters
                types.append(test_type)
                break
    return types

# read the first row and get the types of each column
first_row = next(csv.reader(input_file))
col_types = get_column_types(first_row)

# now create a list of new rows with converted data items
converted = []
for row in csv.reader(input_file):
    # use zip to walk list of converters and list of columns at the same time
    converted_row = [converter(raw_value)
                         for converter, raw_value in zip(col_types, row)]
    converted.append(converted_row)

推荐阅读