Определите все случаи проблемных кавычек - PullRequest
1 голос
/ 25 октября 2019

У меня есть (правильно сформированная) большая строковая переменная, которую я превращаю в списки словарей. Я перебираю массивную строку, разделенную символами новой строки, и запускаю следующую команду list(eval(i)). Это работает в большинстве случаев, но для каждого сгенерированного исключения я добавляю «искаженную» строку в массив failed_attempt. Я уже целый час осматриваю сбойные случаи и считаю, что причиной их сбоя является всякий раз, когда есть дополнительная кавычка, которая не является частью ключей для словаря. Например,

eval('''[{"question":"What does "AR" stand for?","category":"DFB","answers":["Assault Rifle","Army Rifle","Automatic Rifle","Armalite Rifle"],"sources":["https://www.npr.org/2018/02/28/588861820/a-brief-history-of-the-ar-15"]}]''')

Сбой, потому что вокруг «AR» есть кавычки. Если вы замените кавычки одинарными кавычками, например,

eval('''[{"question":"What does 'AR' stand for?","category":"DFB","answers":["Assault Rifle","Army Rifle","Automatic Rifle","Armalite Rifle"],"sources":["https://www.npr.org/2018/02/28/588861820/a-brief-history-of-the-ar-15"]}]''')

Теперь это выполнится успешно.

Аналогично:

eval('''[{"question":"Test Question, Test Question?","category":"DFB","answers":["2004","1930","1981","This has never occurred"],"sources":[""SOWELL: Exploding myths""]}]''')

Сбой из-за кавычек вокруг "Соуэлла", но снова успешно, если вы замените их одинарными кавычками.

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

@ Представление Wiktor почти сработает, но не удастся выполнить следующее:

example = '''[{"question":"Which of the following is NOT considered to be "interstate commerce" by the Supreme Court, and this cannot be regulated by Congress?","category":"DFB","answers":["ANSWER 1","ANSWER 2","ANSWER 3","All of these are considered "Interstate Commerce""],"sources":["SOURCE 1","SOURCE 2","SOURCE 3"]}]'''
re.sub(r'("\w+":[[{]*")(.*?)("(?:,|]*}))', lambda x: "{}{}{}".format(x.group(1),x.group(2).replace('"', "'"),x.group(3)), example)


Out[170]: '[{"question":"Which of the following is NOT considered to be \'interstate commerce\' by the Supreme Court, and this cannot be regulated by Congress?","category":"DFB","answers":["ANSWER 1","ANSWER 2","ANSWER 3","All of these are considered "Interstate Commerce""],"sources":["SOURCE 1","SOURCE 2","SOURCE 3"]}]'

Обратите внимание, что второй набор двойных кавычек на "Interstate Commerce" в ответах не заменяется,

Ответы [ 3 ]

1 голос
/ 25 октября 2019

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

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

Не поможет то, что вы не добавили код в свой вопрос, поэтому эти ответы отрывочны. Если вы зададите https://stackoverflow.com/help/minimal-reproducible-example в своем вопросе с некоторыми минимальными данными - очень минимальными - хорошим, который не вызывает исключения в eval (), и плохим примером, воссоздающим проблему, то я должен быть в состояниичтобы лучше предложить, как применить мой ответ.

Ваш код должен делать что-то вроде этого:

import traceback

sourcesentences = [
     'this is no problem'
     ,"he said 'That is no problem'" 
     ,'''he said "It's a great day"''' 
]

# this is doomed if there is a single or double quote in the sentence
for sentence in sourcesentences:
    words = sentence.split()
    myliststring="[\""+"\",\"".join(words)+"\"]"    
    print( f"The sentence is >{sentence}<" )
    print( f"my string representation of the sentence is >{myliststring}<" )
    try:
        mylistfromstring = eval(myliststring)
        print( f"my list is >{mylistfromstring}<" )
    except SyntaxError as e:
        print( f"eval failed with SyntaxError on >{myliststring}<")
        traceback.print_exc()
    print()

И это приводит к ошибке синтаксиса в третьем предложении теста

Теперь давайте попробуем экранировать символы в переменной, прежде чем заключать их в кавычки:

# this adapts to a quote within the string
def safequote(s):
    if '"' in s:
        s = s.replace( '"','\\"' )
    return s

for sentence in sourcesentences:
    print( f"The sentence is >{sentence}<" )
    words = [safequote(s) for s in sentence.split()]
    myliststring="[\""+"\",\"".join(words)+"\"]"    
    print( f"my string representation of the sentence is >{myliststring}<" )
    try:
        mylistfromstring = eval(myliststring)
        print( f"my list is >{mylistfromstring}<" )
    except SyntaxError as e:
        print( f"eval failed with SyntaxError on >{myliststring}<")
        traceback.print_exc()
    print()

Это работает, но есть ли лучший способ?

Не намного ли проще избежать eval? что означает избегать построения строкового представления списка, что означает избегать проблем с кавычками в тексте:

for sentence in sourcesentences:
    print( f"The sentence is >{sentence}<" )
    words = sentence.split()
    print( f"my list is >{words}<" )
    print()
1 голос
/ 25 октября 2019

Попробуйте, я знаю, это будет работать для всех значений ключа question и category, и я надеюсь, что не забыл ни одного случая для значения lists:

import re


def escape_quotes(match):
    """ espace normal quotes captured by the second group."""
    # match any quote except this quotes : `["` or `","` or `"]`
    RE_ESACEP_QUOTES_IN_LIST = re.compile('(?<!\[)(?<!",)"(?!,"|\])')

    def escape_quote_in_string(string):
        return '"{}"'.format(string[1:-1].replace('"', "'"))

    key, value = match.groups()
    # this will fix for sure the problem related to this keys
    if any(e in key for e in ('question', 'category')):
        value = escape_quote_in_string(value)
    if any(e in key for e in ('answers', 'sources')):
        # keep only [" or "," or "]  escape any thing else
        value = RE_ESACEP_QUOTES_IN_LIST.sub(r"'", value)

    return f'{key}{value}'


# test cases
exps = ['''[{"question":"What does "AR" stand for?"}]''',
        '''[{"sources":[""SOWE"LL: Ex"ploding myths""]}]''',
        '''[{"question":"Test ", Test" Que"sti"on?","sources":[""SOWELL: Ex""ploding myths""]}]''']

# extract key value of the expression you made it easy by specifying that key are fixed
key = '(?:"(?:question|category|answers|sources)":)'
RE_KEY_VALUE = re.compile(rf'({key})(.+?)\s*(?=,\s*{key}|}})', re.S)

# test all cases
for exp in exps:
    # escape normal quotes
    exp = RE_KEY_VALUE.sub(escape_quotes, exp)
    print(eval(exp))

# [{'question': "What does 'AR' stand for?"}]
# [{'sources': ["'SOWE'LL: Ex'ploding myths'"]}]
# [{'question': "Test ', Test' Que'sti'on?", 'sources': ["'SOWELL: Ex''ploding myths'"]}]
0 голосов
/ 25 октября 2019

Если ваш текст каким-то образом хранится в переменной, скажем, в переменной text, вы можете использовать re.sub () :

re.sub('(\s")|("\s)', ' ', text)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...