从csv-file读取数据并转换为正确的数据类型

时间:2012-07-26 08:49:13

标签: python csv python-2.5

我遇到了以下问题。我写了一个二维列表,其中每列具有不同的类型(bool,str,int,list),到csv文件。 现在我想再次从csv文件中读出数据。但我读到的每个单元都被解释为一个字符串。

如何自动将读入数据转换为正确的类型?或者更好:是否有可能告诉csv-reader每列的正确数据类型?

示例数据(如csv文件中):

IsActive,Type,Price,States
True,Cellphone,34,"[1, 2]"
,FlatTv,3.5,[2]
False,Screen,100.23,"[5, 1]"
True,Notebook, 50,[1]

7 个答案:

答案 0 :(得分:12)

作为docs explain,CSV阅读器不会执行自动数据转换。您有QUOTE_NONNUMERIC格式选项,但这只会将所有非引用字段转换为浮点数。这与其他csv读者非常相似。

我不相信Python的csv模块对这个案例会有任何帮助。正如其他人已经指出的那样,literal_eval()是一个更好的选择。

以下工作和转换:

  • 字符串
  • INT
  • 列表
  • 字典

你也可以将它用于布尔值和NoneType,尽管这些必须相应地格式化literal_eval()才能通过。 LibreOffice Calc以大写字母显示布尔值,而在Python布尔值为大写时。此外,您必须使用None(不带引号)替换空字符串

我为mongodb写了一个进口商来完成这一切。以下是我目前编写的代码的一部分。

[注意:我的csv使用tab作为字段分隔符。您可能还想添加一些异常处理]

def getFieldnames(csvFile):
    """
    Read the first row and store values in a tuple
    """
    with open(csvFile) as csvfile:
        firstRow = csvfile.readlines(1)
        fieldnames = tuple(firstRow[0].strip('\n').split("\t"))
    return fieldnames

def writeCursor(csvFile, fieldnames):
    """
    Convert csv rows into an array of dictionaries
    All data types are automatically checked and converted
    """
    cursor = []  # Placeholder for the dictionaries/documents
    with open(csvFile) as csvFile:
        for row in islice(csvFile, 1, None):
            values = list(row.strip('\n').split("\t"))
            for i, value in enumerate(values):
                nValue = ast.literal_eval(value)
                values[i] = nValue
            cursor.append(dict(zip(fieldnames, values)))
    return cursor

答案 1 :(得分:7)

您必须映射您的行:

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)
for row in parsed:
    print row

结果

(True, 'foo', 1, 2.3, 'baz')
(False, 'bar', 7, 9.8, 'qux')

答案 2 :(得分:2)

向Jon Clements和cortopy道具,教我ast.literal_eval!这就是我最终的目标(Python 2; 3的变化应该是微不足道的):

from ast import literal_eval
from csv import DictReader
import csv


def csv_data(filepath, **col_conversions):
    """Yield rows from the CSV file as dicts, with column headers as the keys.

    Values in the CSV rows are converted to Python values when possible,
    and are kept as strings otherwise.

    Specific conversion functions for columns may be specified via
    `col_conversions`: if a column's header is a key in this dict, its
    value will be applied as a function to the CSV data. Specify
    `ColumnHeader=str` if all values in the column should be interpreted
    as unquoted strings, but might be valid Python literals (`True`,
    `None`, `1`, etc.).

    Example usage:

    >>> csv_data(filepath,
    ...          VariousWordsIncludingTrueAndFalse=str,
    ...          NumbersOfVaryingPrecision=float,
    ...          FloatsThatShouldBeRounded=round,
    ...          **{'Column Header With Spaces': arbitrary_function})
    """

    def parse_value(key, value):
        if key in col_conversions:
            return col_conversions[key](value)
        try:
            # Interpret the string as a Python literal
            return literal_eval(value)
        except Exception:
            # If that doesn't work, assume it's an unquoted string
            return value

    with open(filepath) as f:
        # QUOTE_NONE: don't process quote characters, to avoid the value
        # `"2"` becoming the int `2`, rather than the string `'2'`.
        for row in DictReader(f, quoting=csv.QUOTE_NONE):
            yield {k: parse_value(k, v) for k, v in row.iteritems()}

(我有点担心,我可能会错过一些涉及报价的案件。如果你发现任何问题,请发表评论!)

答案 3 :(得分:2)

我知道这是一个相当古老的问题,标记为,但这是适用于Python 3.6+的答案,这可能是使用最新语言版本的人们所感兴趣的。

它利用了Python 3.5中添加的内置typing.NamedTuple类。从文档中可能看不到的是每个字段的“类型”可以是一个函数。

示例用法代码还使用了所谓的f-string文字,这些文字直到Python 3.6才添加,但是不需要进行核心数据类型转换。

#!/usr/bin/env python3
import ast
import csv
import typing


class Rec(typing.NamedTuple):
    """ Define each field and type of a record. """
    IsActive : bool
    Type: str
    Price: float
    States: ast.literal_eval  # To handle list string represenations.


filename = 'test_transform.csv'

with open(filename, newline='') as file:
    for i, row in enumerate(csv.DictReader(file)):
        row = {field: Rec._field_types[field](value)
                  for field, value in row.items()}
        print(f'row {i}:  {row}')

输出:

row 0:  {'IsActive': True, 'Type': 'Cellphone', 'Price': 34.0, 'States': [1, 2]}
row 1:  {'IsActive': False, 'Type': 'FlatTv', 'Price': 3.5, 'States': [2]}
row 2:  {'IsActive': True, 'Type': 'Screen', 'Price': 100.23, 'States': [5, 1]}
row 3:  {'IsActive': True, 'Type': 'Notebook', 'Price': 50.0, 'States': [1]}

答案 4 :(得分:1)

我也非常喜欢@martineau的方法,特别是他的评论使他很感兴趣,他的代码本质是字段和类型之间的清晰映射。那对我来说,字典也可以。因此,他的主题变化如下所示。对我来说很好。

显然,字典中的value字段实际上只是一个可调用的,因此可以用来为数据按摩和类型转换(如果这样选择)提供了一个钩子。

import ast
import csv

fix_type = {'IsActive': bool, 'Type': str, 'Price': float, 'States': ast.literal_eval}

filename = 'test_transform.csv'

with open(filename, newline='') as file:
    for i, row in enumerate(csv.DictReader(file)):
        row = {k: fix_type[k](v) for k, v in row.items()}
        print(f'row {i}: {row}')

输出

row 0: {'IsActive': True, 'Type': 'Cellphone', 'Price': 34.0, 'States': [1, 2]}
row 1: {'IsActive': False, 'Type': 'FlatTv', 'Price': 3.5, 'States': [2]}
row 2: {'IsActive': True, 'Type': 'Screen', 'Price': 100.23, 'States': [5, 1]}
row 3: {'IsActive': True, 'Type': 'Notebook', 'Price': 50.0, 'States': [1]}

答案 5 :(得分:0)

替代使用ast.literal_eval的另一种选择(尽管看起来有点极端)是PyPi上可用的pyparsing模块 - 并查看http://pyparsing.wikispaces.com/file/view/parsePythonValue.py代码示例是否适合于你需要,或者可以很容易地适应。

答案 6 :(得分:0)

我喜欢@martineau的回答。很干净。

我需要做的就是仅转换几个值,并将所有其他字段保留为字符串,例如将字符串作为默认值,并仅更新特定键的类型。

为此,只需替换此行:

row = CSV_Record._transform(row)

通过这个:

row.update(CSV_Record._transform(row))

' update '函数直接更新变量 row ,将来自csv提取的原始数据与'转换为正确类型的值合并_transform ”方法。

请注意,更新的版本中没有' row ='。

希望这对有类似要求的人有帮助。

(PS:我对在stackoverflow上发布内容很陌生,所以如果以上内容不清楚,请告诉我)