python: элемент списка в CSV-файле - PullRequest
2 голосов
/ 30 января 2020

У меня есть csv файл такой структуры:

Id,Country,Cities
1,Canada,"['Toronto','Ottawa','Montreal']"
2,Italy,"['Rome','Milan','Naples', 'Palermo']"
3,France,"['Paris','Cannes','Lyon']"
4,Spain,"['Seville','Alicante','Barcelona']"

Последний столбец содержит список, но он представлен в виде строки, поэтому он обрабатывается как один элемент. При разборе файла мне нужно иметь этот элемент как list, а не строку. Пока что я нашел способ конвертировать это:

L = "['Toronto','Ottawa','Montreal']"
seq = ast.literal_eval(L)

Поскольку я новичок ie в python, мой вопрос - это нормальный способ сделать это, или есть правильный способ представления списков в CSV, чтобы мне не приходилось делать преобразования, или есть более простой способ преобразования?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 30 января 2020

Использование ast.literal_eval(...) будет работать, но для него требуется специальный синтаксис, который не распознается другими программами для чтения CSV, и используют оператор eval, который является красным флагом.

Использование eval может быть опасным, даже если в этом случае вы используете более безопасный вариант literal_eval, который более ограничен, чем функция eval.

Обычно то, что вы видите в файлах CSV, которые иметь много значений в одном столбце в том, что они будут использовать простой разделитель и заключать в кавычки поле.

Например:

ID,Country,Cities
1,Canada,"Toronto;Ottawa;Montreal"

Затем в python или любом другом языке, читать становится тривиально, не прибегая к eval:

import csv

with open("data.csv") as fobj:
    reader = csv.reader(fobj)
    field_names = next(reader)

    rows = []
    for row in reader:
        row[-1] = row[-1].split(";")
        rows.append(row)

Проблемы с ast.literal_eval

Даже если функция ast.literal_eval намного безопаснее, чем обычная eval при вводе пользователем, он все еще может быть использован. В документации для literal_eval есть это предупреждение:

Предупреждение: Возможно обработать sh интерпретатор Python с достаточно большим / сложная строка из-за ограничений глубины стека в компиляторе Python AST.

Демонстрацию этого можно найти здесь :

>>> import ast
>>> ast.literal_eval("()" * 10 ** 6)
[1]    48513 segmentation fault  python

Я определенно не эксперт, но дать пользователю возможность взломать sh программу и потенциально использовать некоторую неясную уязвимость памяти - это плохо, и в этом случае можно избежать.

Если причина, по которой вы хотите использовать literal_eval - для правильного набора текста, и вы уверены, что входные данные надежны на 100%, тогда я думаю, что это нормально. Но вы всегда можете обернуть функцию для выполнения некоторых проверок работоспособности:

def sanely_eval(value: str, max_size: int = 100_000) -> object:
    if len(value) > max_size:
        raise ValueError(f"len(value) is greater than the max_size={max_size!r}")
    return ast.literal_eval(value)

Но, в зависимости от того, как вы создаете и используете файлы CSV, это может сделать данные менее переносимыми, поскольку это python -specifi c формат.

2 голосов
/ 30 января 2020

Если вы можете контролировать CSV, вы можете разделить элементы с другим известным персонажем, которого нет в городе и который не является запятой. Скажите двоеточие (:).

Тогда первая строка, например, будет выглядеть следующим образом:

1,Canada,Toronto:Ottawa:Montreal

Когда дело доходит до обработки данных, у вас будет весь этот элемент , и вы можете просто сделать

cities.split(':')

Если вы хотите go другим способом (у вас есть города в списке Python, и вы хотите создать эту строку), вы можете используйте join()

':'.join(['Toronto', 'Ottawa', 'Montreal'])
0 голосов
/ 30 января 2020

Для заданной c структуры csv вы можете преобразовать города в список следующим образом:

cities = '''"['Rome','Milan','Naples', 'Palermo']"'''

cities = cities[2:-2]  # remove "[ and ]"

print(cities)  # 'Rome','Milan','Naples', 'Palermo'

cities = cities.split(',')  # convert to list

print(cities)  # ["'Rome'", "'Milan'", "'Naples'", " 'Palermo'"]

cities = [x.strip() for x in cities]  # remove leading or following spaces (if exists)

print(cities)  # ["'Rome'", "'Milan'", "'Naples'", "'Palermo'"]

cities = [x[1:-1] for x in cities]  # remove quotes '' from each city

print(cities)  # ['Rome', 'Milan', 'Naples', 'Palermo']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...