#python #string #dictionary
#python #строка #словарь
Вопрос:
Я вызываю функцию, которая возвращает строку, содержащую dict. Как я могу извлечь этот dict, имея в виду, что первая и последняя строки могут содержать ‘{‘ и ‘}’.
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}
This is a {testing string} example
This {is} a testing {string} example
Мне нужно извлечь это значение как переменную dict.
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}
Комментарии:
1. Следует ли обновлять функцию, чтобы возвращать правильный dict? (По сути, решайте проблемы с данными в корне, а не пишите дополнительный код для их решения.)
2. К сожалению, я не могу контролировать вывод функции. Я вызываю команду оболочки, используя подпроцесс, и я ожидаю, что вывод всегда будет в этом формате.
3. Гарантируется ли, что dict всегда будет находиться в одном и том же месте в строке?
4. Итак, мне нужно извлечь dict внутри, чтобы я мог создать правильный вывод, то есть удалить определенные ключи / значения
5. Поскольку ваш ввод может содержать элементы с символами
{
и}
, вам нужно будет найти каждую пару и посмотреть, содержит ли она действительный словарь (и надеюсь, что нет чего-то, что просто выглядит как что-то, что было бы действительным словарем).
Ответ №1:
Обновленный ответ
Принимая во внимание комментарии @martineau и @ekhumoro, следующий отредактированный код содержит функцию, которая выполняет поиск по строке и извлекает допустимые все dict
значения. Это более надежный подход к моему предыдущему ответу, поскольку содержимое реального мира dict
может отличаться, и эта логика (надеется) учитывает это.
Пример кода:
import json
import re
def extract_dict(s) -> list:
"""Extract all valid dicts from a string.
Args:
s (str): A string possibly containing dicts.
Returns:
A list containing all valid dicts.
"""
results = []
s_ = ' '.join(s.split('n')).strip()
exp = re.compile(r'({.*?})')
for i in exp.findall(s_):
try:
results.append(json.loads(i))
except json.JSONDecodeError:
pass
return results
Тестовая строка:
Исходная строка OP была обновлена, чтобы добавить несколько dict
s, числовое значение в качестве последнего поля и list
значение.
s = """
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": 5
}
{"website": "stackoverflow",
"type": "question",
"date": "2020-09-11"
}
{"website": "stackoverflow",
"type": "question",
"dates": ["2020-09-11", "2020-09-12"]
}
This is a {testing string} example
This {is} a testing {string} example
"""
Вывод:
Как указано в OP, в строке обычно есть только один dict
, поэтому к нему (очевидно) можно получить доступ с помощью results[0]
.
>>> results = extract_dict(s)
[{'website': 'stackoverflow', 'type': 'question', 'date': 5},
{'website': 'stackoverflow', 'type': 'question', 'date': '2020-09-11'},
{'website': 'stackoverflow', 'type': 'question', 'dates': ['2020-09-11', '2020-09-12']}]
Оригинальный ответ:
Игнорируйте этот раздел. Хотя код работает, он конкретно соответствует запросу OP и не является надежным для других применений.
В этом примере используется регулярное выражение для определения начала dict {"
и конца dict "}
и извлечения середины, а затем преобразования строки в правильную dict
. Поскольку новые строки мешают и усложняют регулярное выражение, я просто сгладил строку для начала.
Согласно комментарию от @jizhihaoSAMA, я обновил, чтобы использовать json.loads
для преобразования строки в dict
, так как она более чистая. Если вы не хотите дополнительного импорта, eval
также будет работать, но не рекомендуется.
Пример кода:
import json
import re
s = """
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}
This is a {testing string} example
This {is} a testing {string} example
"""
s_ = ' '.join(s.split('n')).strip()
d = json.loads(re.findall(r'({".*"s?})', s_)[0])
>>> d
>>> d['website']
Выводит:
{"website": "stackoverflow", "type": "question", "date": "10-09-2020"}
'stackoverflow'
Комментарии:
1.
eval()
не рекомендуется, попробуйте использовать более безопасную функцию, такую какast.literal_eval()
илиjson.loads
.2. Маловероятно, что реальный ввод операционной системы будет таким же простым, как в тестовом примере, поэтому вероятность того, что такой подход сработает на практике, очень мала.
3. @ekhumoro — Хотя это справедливое предположение в реальном мире, я считаю, что комментарий несправедлив, поскольку это представление может быть применено почти ко всем ответам на SO и сделать их недействительными. Это решение соответствует требованию OP и предоставленному примеру. Все, что выходит за рамки этого, выходит за рамки.
4. Это совсем не так. Обычно можно обобщить тестовые примеры, чтобы учесть большинство реальных возможностей. Если ваше решение может быть признано недействительным из-за незначительных изменений входных данных, значит, оно на самом деле не отвечает требованиям полезным образом. (Например, ваше решение выйдет из строя, если последнее значение в dict окажется числом, а не строкой).
5. @ekhumoro — Точка зрения хорошо принята, и ответ обновлен. Спасибо за расширенное мышление.