Парсинг строк на Python 3.x
Я хочу написать парсер строк, но я реально не знаю, какой модуль использовать и как это реализовать
То есть я хочу получить из, например, hello —world -N -w 5 —Num 5 3 2 и т. д. список [«hello», «big-flag:world», «flag:N», [«flag:w», «5»], [«big-flag:Num», «5»], «3», «2»]
Если ещё разделение по пробелам понятно, то как сделать флаги и другие элементы — не очень
Отслеживать
задан 6 мар 2021 в 10:39
Krutos VIP Krutos VIP
69 2 2 серебряных знака 9 9 бронзовых знаков
Посмотрите на стандартный модуль argparse .
6 мар 2021 в 10:43
Уже смотрел, он дает вроде только уже указанные флаги и параметры. Мне же надо отпарсить любые флаги и параметры, данные юзером. Правда, могу ошибаться
6 мар 2021 в 10:44
Можно получить строку с параметрами через sys.argv[1:] и потом парсить как обычный список (разделение по пробелу). А вообще зачем парсить любые флаги если каждый флаг = какому то действию, а если я передал флаг asd который Вы не запрограммировали что должно произойти?
6 мар 2021 в 11:31
Пытался делать так, но слишком костыльно. А так же, надо мне это для того, чтобы сделать оптимальный парсер не только для одной проги, но и для нескольких Т.е. программа искала именно, то что ей нужно, не прям вот в одном строгом диапазоне как бы
6 мар 2021 в 12:01
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Сделал программу для разбора аргументов командной строки под ваши запросы. Получилась она не маленькой, но рабочей. По ссылке выше я разбил ее на несколько файлов и вообще поместил в отдельный модуль. Ниже приведен аналогичный код одним файлом.
class FToken(object): """Единица лексического разбора; абстрактный базовый класс. Токен имеет свой тип T, по которому его можно идентифицировать, и свое значение value. Для использования вам необходимо определить класс токена и как параметр передать его тип. class MyToken(FToken, type="MyType"): В этом классе необходимо переопределить статические методы can_parse и parse, принимающие один аргумент - экземпляр FParser. Метод can_parse всех дочерник классов будут вызываться в том порядке, в котором эти классы объявлены. Например, в следующем примере класс TEXT никогда не признается токеном. class MyToken(FToken, type="MyType"): @staticmethod def can_parse(parser): return True // . class TEXT(FToken, type="text"): // . """ T = "unknown" def __init__(self, value=None): """Конструктор. @param value Какое угодно значение токена. """ self.value = value def __str__(self): """Возвращает интерпретацию токена как строку.""" return self.T _childs = list() def __init_subclass__(cls, type: str="unknown"): """Инициализирует и регистрирует наследников.""" super().__init_subclass__() cls.T = type cls._childs.append(cls) @staticmethod def can_parse(parser): """Проверяет, возможно ли пролексировать этот токен. @param parser Экземпляр FParser. @return Возвращает True, если возможно; иначе, False. """ return False @classmethod def parse(cls, parser): """Лексирует этот токен. @param parser Экземпляр FParser. @return Возвращает полученный токен или None. """ raise NotImplementedError() #''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''# class FTWhitespaces(FToken, type="whitespaces"): @staticmethod def can_parse(parser): return (parser.char.isspace()) @classmethod def parse(cls, parser): def _(): return parser.char.isspace() parser.parse_by_condition(_) return None class FTParam(FToken, type="param"): def __str__(self): return self.T + ":" + self.value @staticmethod def can_parse(parser): return (parser.char in "'\"" or parser.char.isalnum()) @classmethod def parse(cls, parser): value = str() if parser.char in "'\"": pair = parser.char parser.advance() def _(): return (parser.char != pair) value = parser.parse_by_condition(_) parser.eat(pair) else: value = parser.parse_keyword() return cls(value) class FTBigFlag(FToken, type="big-flag"): def __str__(self): return self.T + ":" + self.value @staticmethod def can_parse(parser): return (parser.char == '-' and parser.peek() == '-') @classmethod def parse(cls, parser): parser.eat('--') key = parser.parse_keyword() return cls(key) class FTFlag(FToken, type="flag"): def __str__(self): return self.T + ":" + self.value @staticmethod def can_parse(parser): return (parser.char == '-') @classmethod def parse(cls, parser): parser.eat('-') key = parser.parse_keyword() return cls(key) #''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''# class FParser(object): """Парсер строк аргументов. """ def __init__(self, arguments: str): self._tokens = list() self.data = arguments self.index = -1 self.char = None if len(arguments) != 0: self.advance() self._parse() self._dispose() def advance(self, n: int=1): """Продвижение вперед. Продвигает переменные вперед; переход к следующему символу. @param n На сколько символов продвинуться. """ self.index += n if self.index >= 0 and self.index < len(self.data): self.char = self.data[self.index] else: self.char = None def eat(self, victim: str): """Поедает ключевую фразу. Если ожидаемая и фактическая фраза расходятся, то вызывается синтаксическое исключение. """ for char in victim: if self.char != char: raise SyntaxError("Unexcepted syntax error.") self.advance() def peek(self): """Предпросмотр следующего символа. @return Возвращает следующий символ или None. """ self.advance() ch = self.char self.advance(-1) return ch def parse_by_condition(self, condition): """Получает ключевую фразу. Ключевая фраза состоит из любых символов, добавляемых, если функция condition возвращает истину (True). Пример использования смотрите в коде FParser.parse_keyword. @param condition Функция-условие, отвечающая за остановку ключевой фразы. Для этого должна вернуть False. @return Возвращает ключевую фразу. """ keyword = str() while self.char != None and condition() == True: keyword += self.char self.advance() return keyword def parse_keyword(self): """Получает ключевое слово. @return Возвращает ключевое слово. """ def _(): return not self.char.isspace() return self.parse_by_condition(_) def _parse(self): while self.char != None: index = self.index for token_cls in FToken._childs: if token_cls.can_parse(self): self._tokens.append(token_cls.parse(self)) break; if index == self.index: ''' Что произошло? - был вызван token_cls.parse, но он ни шиша не сделал. - ни один token_cls.can_parse не вернул True. Что делать? - Вызовем абстрактное исключение лексирования. ''' print(self._tokens) raise SyntaxError(f"Неверный синтаксис на позиции ") def _dispose(self): self.index = None self.char = None self.data = None # Примечание: self._tokens удаляется при getter'е свойства result. @property def result(self): """Выдает результат лексирования. Анализирует полученные токены и превращает их в конечный результат. """ if '_result' in dir(self): return self._result result = list() require_param = False for token in self._tokens: if token == None: continue # whitespaces и другой мусор. if require_param == True: if not isinstance(token, FTParam): break # Go to raise result[len(result) - 1].append(token.value) require_param = False continue if isinstance(token, FTParam): result.append(token.value) elif isinstance(token, (FTBigFlag, FTFlag)): if token.value in ("w", "Num"): result.append([str(token), ]) require_param = True else: result.append(str(token)) else: ''' Программист добавил токены, но не добавил способ их обработки в конечный результат. ''' raise NotImplementedError() if require_param == True: raise Exception("Флаг с параметром не имеет параметра.") _tokens = None self._result = result return result #''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''# import os def GetArgsStr(): result = str() for arg in os.sys.argv: if ' ' in arg: result += "'" + arg + "'" else: result += arg result += ' ' return result.rstrip(' ') def main(): arguments = GetArgsStr() #print("Command line: \"" + arguments + "\"") parser = FParser(arguments) print(parser.result) if __name__ == "__main__": main()
Пример командной строки запуска скрипта:
C:\Users\Shamus_Rezol\Desktop\fargparse>python example.py --world -N -w 5 --Num 5 3 2
Command line: "example.py --world -N -w 5 --Num 5 3 2" ['example.py', 'big-flag:world', 'flag:N', ['flag:w', '5'], ['big-flag:Num', '5'], '3', '2']
Примечание
Создание экземпляра FParser и первое получение результатов - места возможного возникновения исключений.
obj = FParser("example.py --world -N -w 5 --Num 5 3 2") obj.result
Я не учел, что параметр может начинаться со специального символа '/'. Исправить это возможно в tokens.py , FTParam , can_parse . Однако с параметрами вида 'C:/Users' проблем не будет.
Возможно, не учел я и много других мелочей.
Как разделить строку по пробелам python
Такую задачу всегда можно решить с помощью цикла. Но в Python есть метод .split() . Именно его обычно используют для разделения строки на части. В качестве разделителя можно использовать любой символ: пробел, запятую, перенос строки ( \n ) и т.д.
На выходе мы получим список строк:
str = 'how are you' str.split(' ') # ['how', 'are', 'you']
Пробел как разделитель можно не указывать. Метод .split() будет использовать параметр по умолчанию:
str = 'how are you?' str.split() # ['how', 'are', 'you']
Иногда перед тем, как разделить строку, необходимо избавиться от лишних знаков препинания:
str = 'Hi, how are you?' str[:-1].replace(',', '').split() # ['Hi', 'how', 'are', 'you']
Парсинг строки

Парсинг командной строки
Задача такая: При поступлении на вход из командной строки выражения: 1. --key <key> --val.
Парсинг строки с цифирками
Всем привет. В общем есть програмка, которая запрашивает у юзера права(uuid,guid и тд) на файл в.

Парсинг сложной строки
Добрый день, недавно начал заниматься новым для себя языком ( phython): облазил инет не нашёл.
Парсинг строки, вывод построчно
python 3.7 подскажите, есть большая строка, как с помощью регулярных выражений или как нибудь.

Парсинг строки через re
Доброго времени суток. Имеется строка такого вида: "hello-all-world". Как мне извлечь all через re.
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь
Парсинг строки с использованием xPath - Python
Добрый день! Вопрос заключается в следующем. Возможно ли получить вторую строчку Progress: с.

Парсинг по порядку или парсинг до определенного тега
Доброго времени друзья! Подскажите пожалуйста, к примеру есть вот такой html код: <div.

Парсинг JSON ответа. Парсинг строки с массивами
Есть ответ с сервера в виде json: ,\"Polygon\":,]>", "OrganizationId": 2, .

Парсинг JSON-строки и вывод данных из строки
Добрый день! Я разрабатываю Android-клиент, который мог бы обмениваться данными с размещенной.
Как парсить строки с помощью Python?
Как с помощью Python парсить строки? Например, приходит инф с адресом "Киевская обл. , г. Киев, ул. Крещатик, дом 50, кв. 8" по факту этот адрес должен быть связан с ФИО клиента в БД. Но так как в БД залили не такой формат, то по поиску в БД не ищет ибо адрес может быть в другом порядке записан или без указания букв "кв", "дом" и тд.
- Вопрос задан более трёх лет назад
- 1292 просмотра
2 комментария
Средний 2 комментария
Уровень изложения на уровне.
Нужен пример входной строки и то, что нужно получить на выходе

A1K0 @A1K0 Автор вопроса
Входной строки с бд? сори я пока не очень понимаю.
За изложение тоже сори, по той дже причине)
Коротко изложить, то нам нужно просто найти совпадения в БД и распарсить на нужные строки типа "область, город, улица. Дом. кв"
Решения вопроса 2

ScriptKiddo @ScriptKiddo
Для начала вам нужно нормализовать адреса в базе. Сделать это можно, например, с помощью геокодера от Яндекса
/geocoder
Например, ищем "Москва, Тверская 6"
< "response": < "GeoObjectCollection": < "metaDataProperty": < "GeocoderResponseMetaData": < "request": "Москва,Тверская 6", "results": "10", "found": "1" >>, "featureMember": [ < "GeoObject": < "metaDataProperty": < "GeocoderMetaData": < "precision": "exact", "text": "Россия, Москва, Тверская улица, 6с1", "kind": "house", "Address": < "country_code": "RU", "formatted": "Россия, Москва, Тверская улица, 6с1", "postal_code": "125009", "Components": [ < "kind": "country", "name": "Россия" >, < "kind": "province", "name": "Центральный федеральный округ" >, < "kind": "province", "name": "Москва" >, < "kind": "locality", "name": "Москва" >, < "kind": "street", "name": "Тверская улица" >, < "kind": "house", "name": "6с1" >] >, "AddressDetails": < "Country": < "AddressLine": "Россия, Москва, Тверская улица, 6с1", "CountryNameCode": "RU", "CountryName": "Россия", "AdministrativeArea": < "AdministrativeAreaName": "Москва", "Locality": < "LocalityName": "Москва", "Thoroughfare": < "ThoroughfareName": "Тверская улица", "Premise": < "PremiseNumber": "6с1", "PostalCode": < "PostalCodeNumber": "125009" >> > > > > > > >, "name": "Тверская улица, 6с1", "description": "Москва, Россия", "boundedBy": < "Envelope": < "lowerCorner": "37.607242 55.757926", "upperCorner": "37.615452 55.762556" >>, "Point": < "pos": "37.611347 55.760241" >> > ] > > >
После чего загружаете в базу в нужной схеме и ищете по нормализованным данным
Ответ по адресу из вопроса
< "response": < "GeoObjectCollection": < "metaDataProperty": < "GeocoderResponseMetaData": < "request": "Киевская обл. , г. Киев, ул. Крещатик, дом 50, кв. 8", "results": "10", "found": "2" >>, "featureMember": [ < "GeoObject": < "metaDataProperty": < "GeocoderMetaData": < "precision": "exact", "text": "Украина, Киев, улица Крещатик, 50", "kind": "house", "Address": < "country_code": "UA", "formatted": "Украина, Киев, улица Крещатик, 50", "Components": [ < "kind": "country", "name": "Украина" >, < "kind": "province", "name": "Киев" >, < "kind": "locality", "name": "Киев" >, < "kind": "street", "name": "улица Крещатик" >, < "kind": "house", "name": "50" >] >, "AddressDetails": < "Country": < "AddressLine": "Украина, Киев, улица Крещатик, 50", "CountryNameCode": "UA", "CountryName": "Украина", "AdministrativeArea": < "AdministrativeAreaName": "Киев", "Locality": < "LocalityName": "Киев", "Thoroughfare": < "ThoroughfareName": "улица Крещатик", "Premise": < "PremiseNumber": "50" >> > > > > > >, "name": "улица Крещатик, 50", "description": "Киев, Украина", "boundedBy": < "Envelope": < "lowerCorner": "30.516022 50.440632", "upperCorner": "30.524232 50.445875" >>, "Point": < "pos": "30.520127 50.443254" >> >, < "GeoObject": < "metaDataProperty": < "GeocoderMetaData": < "precision": "other", "text": "Украина, Киевская область", "kind": "province", "Address": < "country_code": "UA", "formatted": "Украина, Киевская область", "Components": [ < "kind": "country", "name": "Украина" >, < "kind": "province", "name": "Киевская область" >] >, "AddressDetails": < "Country": < "AddressLine": "Украина, Киевская область", "CountryNameCode": "UA", "CountryName": "Украина", "AdministrativeArea": < "AdministrativeAreaName": "Киевская область" >> > > >, "name": "Киевская область", "description": "Украина", "boundedBy": < "Envelope": < "lowerCorner": "29.266411 49.179114", "upperCorner": "32.161466 51.554013" >>, "Point": < "pos": "30.456149 50.29807" >> > ] > > >
UPD: добавил ответ по адресу из вопроса.
Ответ написан более трёх лет назад
Нравится 3 2 комментария

ScriptKiddo, отличная мысль!
Если не ошибаюсь, геокодер отдаёт список совпадений. Можно брать первый результат в ответе. Но будет ли он правильным? ScriptKiddo, интересует твой опыт)
И кроме того, у геокодера есть ограничения по числу запросов. Хотя думаю их хватит, просто придётся парсить на протяжении нескольких дней.
Мне тоже интересна тема с обработкой адресов. Буду рад любым советам)

ScriptKiddo @ScriptKiddo
Ограничение - 25K запросов в сутки, но если результаты будете использовать в коммерческой деятельности, то все равно придется использовать платную версию API

A1K0, добро пожаловать в дивный мир распарсивания адресов)
Это "кроличья нора".
В идеале адреса в базу данных должны писаться через справочники (КЛАДР, ФИАС): выбираешь из словаря город, потом улицу и т.д.
Если же у тебя адрес зашит в одну строку, в одно текстовое поле в БД, то пользователи могут писать адрес всевозможными способами. Часто имеют место ошибки в написании улиц, использование черточек, дефисов, дробей, запятых, точек с запятой. Если база содержит сотни тысяч записей, то выверить все ошибки будет очень долго и муторно.
Если адрес строго формализован, то проблем почти нет. Парсишь через разделитель.
Но перед этим советую посчитать число этих разделителей в каждой строке. Может оказаться так, что у тебя в одну запись попали два адреса (регистрации и фактического проживания).
list_address = ["Киевская обл. , г. Киев, ул. Крещатик, дом 10, кв. 8", "Киевская обл. , г. Киев, ул. Крещатик, 2, 12", "Киевская обл. , г. Киев, ул. Крещатик, д.20, кв.118", "Киевская обл. ; Киев; Крещатик; 50-8", "Киевская обл. , г. Киев, пл. Незалежности, д.12-а, строение 3, помещение 8, офис. 33",] for adr in list_address: # допустим разделитель запятая # и корректный адрес должен состоять из 5 частей region, city, street, house, flat = "", "", "", "", "", if adr.count(",") == 4: adr = adr.split(",") region = adr[0] city = adr[1] street = adr[2] house = adr[3] flat = adr[4] print(region, city, street, house, flat) # распарсятся три адреса и пяти: # Киевская обл. г. Киев ул. Крещатик дом 10 кв. 8 # Киевская обл. г. Киев ул. Крещатик 2 12 # Киевская обл. г. Киев ул. Крещатик д.20 кв.118
Ответ написан более трёх лет назад
Комментировать
Нравится 1 Комментировать
Ответы на вопрос 1

Dimonchik @dimonchik2013
non progredi est regredi
но не копипастом функции, а пониманием исходных данных и методов преобразования
кому тамита парсер понадобится, а кому - простое векторное расстояние названия улицы от массива улиц в базе