как я превратил неорганизованный текстовый файл в аккуратный файл CSV

kak ya prevratil neorganizovannyj tekstovyj fajl v akkuratnyj fajl csv

Поэтому я решил изучить Python. Оказывается, этот язык компьютерного программирования не так уж сложен (ну, пока я не получил этот проект! 😛 ).

За считанные секунды я влюбился в его простой, четкий синтаксис и автоматическое отступление во время написания. Я был очарован, когда узнал, что структуры данных, такие как списки, кортежи и словарь, можно создавать и инициализировать динамически с помощью одной строки (например, имя списка = [] ).

Более того, к содержащимся в них значениям можно было получить доступ с использованием индексов или без них. Это делает код хорошо читаемым, поскольку индекс заменяется английским словом по выбору.

Ну, достаточно сказано о языке. Позвольте мне показать вам, что требует проект.

Этот проект подарил мне мой брат. Он наткнулся на текстовый файл, содержащий тысячи слов. Многие слова имели почти то же значение. Каждое слово имело свое определение и пример предложения рядом, но не очень организованно. Между словом и его предложением были пробелы и новые строчки. Некоторые аспекты отсутствовали в словах. Ниже приведены фрагменты текстового файла, о котором я говорю:

WGYzW5bwRlbKbT1pJrheXmw4fiyw6pZxCygw
Здесь «Glower» предшествует новая строка, в то время как другие — нет
FN02Y6fnT3JCd7o6-ld2BmvCrjMPjKoicKZ9
Здесь перед примером ‘Shirk’ ставится новая строчка

Он хотел, чтобы аспекты текста были однородными. Для этого ему понадобилось, чтобы я аккуратно отсортировал все подобные по значению слова рядом с темой. Он сказал мне, что этого можно добиться, собрав все данные в тексте в словарь в следующем формате:

KDTCLHvKcVqMomXywBEHEFy-8dTuhgNHsh1o
«Тема» – это ключ, а остальное – значение ключа. Формат становится несколько сложнее в поддержке, поскольку каждая тема содержит несколько слов

а затем записать их в файл CSV (значения, разделенные запятыми).

Он спросил, могу ли я считать это своим первым проектом, теперь, когда я научился основам. Я был в восторге от разработки логики, и потому мгновенно согласился. Когда его спросили о дедлайне, он дал мне приличное время на 2 дня.

К сожалению, в итоге мне понадобилось двойное количество времени, так как я старался правильно отладить написанный код. Честно говоря, если бы не короткие визиты моего брата в мою комнату, чтобы посмотреть на прогресс и не намекать на неправильные предположения, сделанные мной при написании условий, мне суждено было завершить проект в вечности 😛

Я начал с создания мини-задач в программе, которую я стремился завершить, прежде чем создать всю программу. Ниже приведено:

1. Формирование регулярного выражения для соответствия числу и слову рядом.

Я просмотрел текстовый файл и увидел, что любая тема (здесь именуемая как «ключ») имеет номер перед ней. Итак, я написал несколько строк кода для создания регулярного выражения (регулярное выражение – мощный инструмент для извлечения текста) шаблона следующим образом:

cqV13FJrWa-utLtiM1D-p4ESdMjmw5lIrFGi
Приведенный выше код находит совпадения в соответствии с регулярным выражением и добавляет его в список строк.

Однако когда я запустил это, я получил ошибку, UnicodeDecodeError, точнее, что означало, что я не имел доступа к текстовому файлу. Я просмотрел это, и после долгих поисков безуспешно мой брат пришел и нашел решение. Ошибка была исправлена ​​следующим образом:

wrNyd79CnlLaKQ2Jvy0ljZQOsEHWF9Zgj0jK
Забавно, как одна строка – «ошибки = ‘заменить»» сделала свою работу

Всё равно я не получил желаемого результата. Это произошло потому, что некоторые ключи имели косые риски (‘/’) или пробелы (») в тексте, которым мое регулярное выражение не могло совпадать. Я думал улучшить выражение регулярного выражения позже, и потому написал комментарий рядом с ним.

2. Получение списка строк в виде строк из текстового файла

Для этого я написал только 1 строчку кода, и, к счастью, никаких ошибок не обнаружилось.

qIn0n5s6L9QG4OGvy3sHXRbIAH4JxysGGPJq
Иногда все, что вам нужно, это 1 строчка кода!

Однако я получил нечистый список. Он содержал новые строки (‘\n’) и пробелы (»), затем я пытался уточнить список следующим образом:

-JeEJnbwq-Htyg1W0zRIyMjCCBWGB-2BlGrW
Первый цикл ‘for’ заменяет ‘\n’ на », а второй удаляет », таким образом давая чистый список.

3. Отдельно выделение слов, значений и примеров предложений и добавление их в соответствующие списки.

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

Интересно, что просматривая текстовый файл, я заметил больше шаблонов. Каждое слово имело значение в одной строке, разделенной знаком «=». Кроме того, каждому примеру предшествовали знак «:» и ключевое слово «Пример».

Я думал снова использовать регулярное выражение. Я нашел альтернативное и более элегантное решение, разрезав строку (теперь строчку в списке) в соответствии с размещением символов. Нарезка – еще одна интересная функция в Python. Я написал код так:

OsCkivyCNPKH6p2ntAg5zF-ouq3krcII1AAf
Нарезка собирает два индекса и вытягивает строчку между ними

Приведенный выше код читается почти как английский. Для каждой строки в чистом списке он проверяет, есть ли в нем знак «=» или «:». Если это так, то индекс знака найден, и нарезка выполняется соответственно.

В первом ‘if’ часть перед ‘=’ хранится в переменной ‘word’, а часть после нее хранится в ‘meaning’. Аналогично для второго ‘if’ (‘elif — else if — в этом случае) часть после ‘:’ сохраняется в ‘example’. После каждой итерации слово, значение и пример предложения хранятся в соответствующих списках. Таким образом можно извлечь все данные.

Всё идет нормально. Но я заметил, что извлечение должно быть выполнено таким образом, чтобы каждое слово (и его аспекты) отдельного ключа было накоплено вместе как одно значение для ключа. Это означало, что нужно было хранить каждое слово, значение и пример внутри кортежа. Каждый кортеж должен храниться в одном списке, который будет представлять себя как значение для определенного ключа. Это показано ниже:

Li4Hhl0IG-o9j7RBl-1NkBOx2y6PD2epxwD6
Формат для хранения всех данных в виде одного значения для определенного ключа

Для этого я планировал собрать каждое слово, значение и предложение каждого ключа внутри отдельного списка, охваченного другим списком, скажем, списком ключей. Опять же картинка скажет вам точнее:

G-KkcM7xNm0WEvziO9sar31JvRYVYA34IApF
Отдельный список слов для каждого ключа и так же для значений и примеров

Для этого я добавил следующий код к тому, что написал для нарезки:

mJ7RVqA9LAd6OGtVi06muhAfceffajLHejDv
Делать правильные вещи не всегда легко, а то, что смотрится легко, не всегда правильно!

К сожалению, логика этого кода (другая часть) оказалась неверной. Я ошибочно предположил, что в тексте было только 2 условия (‘=’ и ‘:’). Было много исключений, которые я не заметил. В итоге я потратил часы на отладку возможных ошибок в логике. Я предполагал, что полный текстовый файл имеет ту же схему. Но это было не так.

Не имея возможности добиться прогресса, я перешел в следующую часть программы. Я думал, что мне может понадобиться помощь от моего брата после завершения других частей. 😛

Продолжение следует…

4. Создание значений для ключей с помощью функции Zip и распаковки параметров.

На этом этапе я не был полностью уверен, что я буду делать даже после достижения приведенной выше конфигурации списков. Я узнал о функции «Zip» и «Распаковка параметров» во время одного из технических разговоров моего брата, буквально сжавшего списки, передаваемые ей, например:

tSOhmVSt8tQUutN0wF6j2W2vFF-5lk0xQIqm
Функция Zip в Python

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

WqXdaySyVeD3oLPhqflTj-AEyj0utRmLREnq
Этот фрагмент кода работал безупречно! 🙂

Работу приведенного выше кода можно понять, посмотрев на выход:

4NPeKGnsn38Pb4zE3hkvrEwYc3k20H6tC10i
List5 – необходимый конечный результат

Функция zip() архивирует соответствующие списки или значения в списках и укладывает их в кортеж. Кортежи внутри списков затем преобразуются в списки для распаковки и последующего архивирования. Наконец, желаемый результат был получен.

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

5. Назначение значений клавишам в словаре.

Для этого я пришел к этому решению после некоторых экспериментов с кодом:

Y4PFIu20LiR4BU2ZdEM4Twg6PDLP9i3v33mW

Это дало желаемый результат следующим образом:

mb9BFyytdkVeneMWHhz6bpGspfZx59PldNSK
Этот результат основан на бета-файле, содержащем предварительные и текущие фрагменты кода.

Программа была практически готова. Основная проблема заключалась в части извлечения данных.

… продолжение из раздела 3

После часов и часов отладки я все больше огорчался, почему эта чертова вещь не работает. Я позвонил своему брату, и он дал мне тонкую подсказку о предположениях, которые я сделал при определении условных циклов и предложений if-else. Мы внимательно рассмотрели текстовый файл и заметили, что некоторые слова содержат примеры в двух строчках вместо одной.

YFJG3fz10RxPNE2foL54IrEM9cxvmgRR9fG6
Здесь примеры предложений для «Ладан» и «Ир» занимают две строчки вместо одной.

Согласно моей логике кода, поскольку во второй строке нет знака «:» (как и знака «=», если на то пошло), содержимое строки не будет рассматриваться как часть примера. В результате этот оператор сделает последнюю часть «else» истинной и выполнит написанный в ней код. Учитывая все это, я изменил код, как показано ниже:

XOVv2Ct5wZfEbwX5dxHCAEg9zIXFldxBgkRq
Модификация — вставлено другое условие ‘elif’ и переписан код для части ‘else’

Здесь hasNumbers() — это функция, проверяющая, есть ли в данной строке числа. Я определил это так:

ywXKLxpOnJxn21dZM-hGRcYROoFqDtDTNlNi

Это означает, что он собирает вторую строку примера, если все остальные условия не отвечают требованиям, объединяет ее с первой строкой, а затем добавляет ее в соответствующий список по-прежнему.

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

После часов безумия мой брат показал мне способ получить номера строк, где произошла ошибка. Одним из основных навыков программирования является умение отлаживать программу, правильно проверять возможные ошибки и поддерживать непрерывный поток.

Интересно, что следующее дополнение к коду сообщает, что ошибка произошла примерно в строке 1750 текстового файла.

rPvQnIMvfADSeUpBNr7PExQy1RFiqvH7PPbl
Функция Enumerate добавляет счетчик к iterable, что позволяет легко отлаживать, проверяя номер строки, где произошла ошибка.

Это означало, что программа хорошо работала до этого номера строки и что мой код был правильным! Проблемы заключались в моих неправильных предположениях, а также в текстовом файле благодаря его неоднородности.

В этот раз я заметил, что некоторые ключи были не по номерам, что вызывало проблемы в логическом потоке. Я исправил ошибки, изменив код следующим образом:

Ey2TyrvmuVoQZX5TlYPrHDKOXzjhq9Htn3PI
Это тоже хорошо работало некоторое время, но потом вдруг!

Это хорошо работало до строки 4428 текстового файла, но сразу после этого вышло из строя. Я проверил номер строки в самом текстовом файле, но это не помогло. Тогда я понял, к моему счастью, что это, пожалуй, последняя строчка. Вся программа работала над чистым списком, не имевшим новых строк и пробелов. Я напечатал последнюю строку чистого списка и сравнил его с последней строкой текстового файла. Они совпали!

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

Поэтому я написал дополнительную строку кода, чтобы скрыть это:

ymJP4egnknSAv1QgKxRTl-ugOPQrF0ZVOnke

Сейчас все заработало. Наконец-то! Теперь мне осталось только назначить ключи соответствующим значением и все! В этот момент я сделал перерыв, полагая, что мой проект наконец-то закончился. Позже я добавлю последние штрихи.

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

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

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

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

Еще через несколько часов я смог наконец завершить свою программу (но не пока я потратил 4 полных дня). Я сделал еще несколько изменений, таких как:

i) изменение функции hasNumbers на функцию hasNumbersDot и исключение регулярного выражения, которое я сделал ранее в программе. Это более эффективно соответствовало ключам, поскольку не было предположений, а следовательно, и исключений. Код для него таков:

IVIaKsOIkEAfhxoL737S0YMNGtUfNfjgIlN5
Это возвращает ‘True’, если оно соответствует строкам, содержащим число и слово в начале строки, а не предыдущей, соответствующей тому же в любом месте строки.

ii) замена условия регулярного выражения и кода для получения ключей из чистого списка.

Cd-MM7Zu99xYidQZ82J1duxfyuejp8xtvphe
Заменил это кодом ниже
dDKUZ56tPnPXy7K2eSCDX-YkGsOJW0prraFX
Это намного эффективнее предыдущего, поскольку использует уже определенную функцию ‘hasNumbersDot’. Кроме того, он соответствовал полной строке (полный ключ)

iii) сочетание условий «if» в части «изъятия примеров».

L4mhW4jQTRtYajUyi1SPyfhCP9hQxorFshok
До этой модификации последнее предложение примера нельзя было извлечь из каждого ключа.

iv) материализация кода для назначения ключей словаря

ee6HfHmapwAOpPMVcQOlRLB9-4CBKmNlizdI

Кроме того, после некоторых проб и ошибок мне удалось превратить полученные данные в красиво структурированный CSV файл:

SedcM6tUH30EzegWEn4W44KOiwaMW7Pqoj7T
8rTek-mWVCF4izuq8mNX8a3cHsfcguXtFgV8
Код для получения файла CSV

Вы можете просмотреть мое хранилище github в моем профиле, чтобы просмотреть полный код программы, включая текстовый файл и csv.

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

Остальное! Недавно я встретил веселый мем об этапах отладки, который настолько связан с моим опытом, что я не могу удержаться от разделения. xD

wliLCjiLP7NjE27uYnBliU7ovlILBPI-xc6r

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

Добавить комментарий

Ваш адрес email не будет опубликован.