Как разобрать файл CSV с разными элементами строки без использования внешней библиотеки?

Я пытаюсь разобрать файл CSV в Python; элементы в файле увеличиваются после первой строки с 6 до 7.

Пример CSV:

Title,Name,Job,Email,Address,ID
Eng.,"FirstName, LastName",Engineer,[email protected],ACME Company,1234567
Eng.,"FirstName, LastName",Engineer,[email protected],ACME Company,1234567

Мне нужен способ форматирования и представления вывода в виде чистой таблицы.

Насколько я понимаю, проблема с моим кодом заключается в том, что, начиная со второй строки, элементы CSV увеличиваются с 6 до 7. Таким образом, он выдает следующую ошибку.

print(stringFormat.format(item.split(',')[0], item.split(',')[1], item.split(',')[2],
                          item.split(',')[3], item.split(',')[4], item.split(',')[5],))
IndexError: list index out of range

Мой код:

stringFormat = "{:>10} {:>10} {:>10} {:>10} {:>10}  {:>10}"

with open("the_file", 'r') as file:
     for item in file.readlines():
            print(stringFormat.format(item.split(',')[0], item.split(',')[1],
                                      item.split(',')[2], item.split(',')[3],
                                      item.split(',')[4], item.split(',')[5],
                                      item.split(',')[6]))

Когда вы видите кавычки в файле csv, это означает, что все, заключенное в кавычки, является одно поле, даже если оно может содержать запятую внутри кавычек. Таким образом, ваши строки имеют только 6 столбцов, как и ваш заголовок. Библиотека csv является частью стандартной библиотеки Python (это не внешняя библиотека). Используйте это, он обрабатывает правильный анализ файлов csv, включая кавычки. Если вам не разрешено даже использовать это, вам придется подумать о том, как избежать разделения строки на запятую, заключенную в кавычки.

Pranav Hosangadi 17.03.2022 18:23

@PranavHosangadi, спасибо за ответ, но я не хочу использовать какую-либо библиотеку. Мой код работает, за исключением того, что после первой строки я получаю ошибку из-за увеличенного элемента (ID). Попробуйте код. И вы увидите, что я имею в виду.

Al1nuX 17.03.2022 18:26

@Al1nuX: то, что ошибки не было, не означает, что ваш код работал правильно.

martineau 17.03.2022 18:52

Если кто-то попытается запустить код, поймет, в чем проблема. Код может работать только с фиксированным числом индексов. Как только индекс увеличится, он выдаст ошибку. Мне нужно решение, которое можно настроить с изменением index.

Al1nuX 17.03.2022 18:55

Интересно, кто минусовал? по крайней мере, попытайтесь внести свой вклад или перестаньте отрицать вопросы других людей. Что не так ясно, бесполезно или без усилий в вопросе?

Al1nuX 17.03.2022 19:30

Я не понизил голосование, но думаю, что причина, по которой кто-то это сделал, заключалась в том, что вы неправильно поняли проблему (или полностью искусственное ограничение неиспользования внешних библиотек).

martineau 17.03.2022 19:59

@martineau, и именно поэтому я прошу помощи в первую очередь.

Al1nuX 18.03.2022 08:42
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
7
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете попробовать что-то вроде этого. Цикл for использует длину разделенного элемента, поэтому вы можете иметь строки переменной длины.

stringFormats = ["{:>10}", "{:>10}", "{:>10}", "{:>10}", "{:>10}", "{:>10}", "{:>10}"]

with open("the_file", 'r') as file:
    for item in file.readlines():
        s_item = item.split(',')
        f_item = ''
        for x in range(len(s_item)):
            f_item += stringFormats[x].format(s_item[x])
        print(f_item)

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

stringFormat = "{:>10}"

with open("the_file", 'r') as file:
    for item in file.readlines():
        s_item = item.split(',')
        f_item = ''
        for a_field in s_item:
            f_item += stringFormat.format(a_field)
        print(f_item)

Спасибо за ответ. Ваш код работает! Однако я не понимаю, что означают s_item и f_item. Кроме того, между отображаемыми элементами нет линий; как я могу указать и настроить пробелы между ними? И последнее, как я могу удалить " " из "Имя, Фамилия" в выводе?

Al1nuX 17.03.2022 19:02
Ответ принят как подходящий

Вы можете сделать это с помощью очень простых циклов for, как показано ниже. Я добавил оператор печати, чтобы показать эффекты

# 'r' is not needed, it is the default value if omitted
with open("file_name") as infile:
    result = []
    # split the read() into a list of lines
    # I prefer this over readlines() as this removes the EOL character
    # automagically (I mean the `\n` char) 
    for line in infile.read().splitlines():
        # check if line is empty (stripping all spaces)
        if len(line.strip()) == 0: 
            continue
        # another way would be to check for ',' characters
        if ',' not in line:
            continue
        # set some helper variables
        line_result = []
        found_quote = False
        element = ""
        # iterate over the line by character
        for c in line:
            # toggle the found_quote if quote found
            if c == '"':
                found_quote = not found_quote
                continue
            if c == ",":
                if found_quote:
                    element += c
                else:
                    # append the element to the line_result and reset element
                    line_result.append(element)
                    element = ""
            else:
                # append c to the element
                element += c
        # append leftover element to the line_result
        line_result.append(element)
        
        # append the line_result to the final result
        result.append(line_result)
        print(len(line_result), line_result)


print('------------------------------------------------------------')
stringFormat = "{:>10} {:>20} {:>20} {:>20} {:>20}  {:>10}"

for line in result:
    print(stringFormat.format(*line))

выход

6 ['Title', 'Name', 'Job', 'Email', 'Address', 'ID']
6 ['Eng.', 'FirstName, LastName', 'Engineer', '[email protected]', 'ACME Company', '1234567']
6 ['Eng.', 'FirstName, LastName', 'Engineer', '[email protected]', 'ACME Company', '1234567']
------------------------------------------------------------
     Title                 Name                  Job                Email              Address          ID
      Eng.  FirstName, LastName             Engineer    [email protected]         ACME Company     1234567
      Eng.  FirstName, LastName             Engineer    [email protected]         ACME Company     1234567

Некоторые корректировки после разговора.

Примечания о сортировке списка списков. Он будет сравнивать первый элемент внутренних списков друг с другом. Если они совпадают, он будет сравнивать второй элемент внутренних списков друг с другом и т. д. Из-за этого вы можете захотеть переместить столбец идентификатора во второй столбец в результирующем списке, так как это, кажется, то, что называется уникальным идентификатор (UID).

with open("file_name") as infile:
    lines = infile.read().splitlines()

# set the header and remove it from lines.
header = lines.pop(0).split(',')
# rearrange the header to put the last element (date) first
# -1 gets the last element (eg, count from end)
header.insert(0, header.pop(-1))

# store the header length as this will speed up the process for longer files
# otherwise you would have to call len(header) in each iteration of the loop
header_len = len(header)

result = []
for line in lines:
    if ',' not in line:
        continue
    # split the line once here, so we don't have to split it a million
    # times in the rest of the loop
    split_line = line.split(',')
    if len(split_line) > header_len:
        # note, you can remove the strip('"') if you want to keep the quotation marks
        # also note that .pop() removes the element "in place", which is why I
        # use .pop(1) twice. first time it gets firstname, second time it gets lastname
        split_line.insert(1, f"{split_line.pop(1)},{split_line.pop(1)}".strip('"'))
    # move the date element to the start
    split_line.insert(0, split_line.pop(-1))
    # do some slicing on the date element to turn it into YYYYMMDD as this allows for
    # proper sorting without any hassle. I'm assuming the date you provided is in the format
    # MM/DD/YYYY. You can easily move the order around if it's DD/MM/YYYY
    # Also, pad day/month with leading zero's using f"{string:>02}"
    split_line[0] = f"{split_line[0].split('/')[2]}{split_line[0].split('/')[0]:>02}{split_line[0].split('/')[1]:>02}"
    result.append(split_line)

# sort it. Since the date is in numeric format, and the first element, it sorts 
# properly automagically
result.sort()

# if you want you can re-format the date again. you can do so with some list slicing
# since the date string is now properly formatted this is very easy to do
# because the sort() above happens outside the initial loop, we cannot do it inside said loop
for line in result:
    line[0] = f"{line[0][6:]}/{line[0][4:6]}/{line[0][0:4]}"

# insert the header
result.insert(0, header)


stringFormat = "{:>10} {:>25} {:>20} {:>20} {:>20} {:>10} {:>10}"
for line in result:
    print(stringFormat.format(*line))


# write it as a CSV file with ; used as separator instead
with open("output.csv", "w") as outfile:
    for line in result:
        outfile.write(";".join(line) + "\n")

Тоже отличное решение. действительно полезно. Можете ли вы отредактировать код, чтобы включить ( с оператором open («file_name», 'r') as file: ), пожалуйста?

Al1nuX 17.03.2022 19:17

готово, обратите внимание, что 'r' не требуется при открытии файлов для чтения...

Edo Akse 17.03.2022 19:23

Я бы проголосовал 2 раза, если бы мог! спасибо, брат, это поможет мне больше узнать о чтении файлов и управлении ими. Однако в конце я получаю сообщение об ошибке: print(stringFormat.format(*line)) IndexError: индекс замены 1 вне диапазона для кортежа позиционных аргументов

Al1nuX 17.03.2022 19:29

Я думаю, что у вас могут быть пустые строки во входном файле, я добавил два примера проверки работоспособности непосредственно под первым циклом for. Также обратите внимание, что вы можете принять ответ вместо голосования :-)

Edo Akse 17.03.2022 19:34

Если последний столбец справа — это таблица дат, то можно ли ее вывести влево и сделать первой таблицей, отсортировав даты по порядку сверху вниз? и записать в файл?

Al1nuX 17.03.2022 23:11

Да всему этому. Нарезка списка - ваш друг, чтобы изменить порядок и дату. Для сортировки это немного зависит от того, как на самом деле отформатирована дата. Вы всегда можете просто импортировать дату и время и преобразовать ее в объект даты и времени, что упрощает обработку. Можете ли вы предоставить образец фактической строки для даты?

Edo Akse 18.03.2022 00:43

Давайте продолжить обсуждение в чате.

Al1nuX 18.03.2022 08:46

Я получаю эту ошибку «print (stringFormat.format (* line)) IndexError: индекс замены 6 вне диапазона для кортежа позиционных аргументов»

Al1nuX 18.03.2022 11:48

вы изменили stringFormat? Я добавил один элемент по мере добавления столбца даты. Если нет, попробуйте распечатать строку для отладки, чтобы увидеть, соответствует ли строка, выдающая ошибку, тому, что вы ожидаете.

Edo Akse 18.03.2022 12:14

Я удалил один из "{:> 10}", теперь он работает.

Al1nuX 18.03.2022 12:55

Другие вопросы по теме