9
votes

Comment scinder des paires de valeurs clés séparées par des virgules avec des virgules citations

Je sais qu'il y a beaucoup d'autres postes sur l'analyse de valeurs séparées par des virgules, mais je ne pouvais pas en trouver un qui divise des paires de clés et des poignées de virgules citées.

J'ai des cordes comme ceci: p> xxx pré>

et je veux obtenir ceci: p> xxx pré>

J'ai essayé d'utiliser shlex code> comme ceci: p >

lexer = shlex.shlex('''age=12,name=bob,hobbies="games,reading",phrase="I'm cool!"''')
lexer.whitespace_split = True
lexer.whitespace = ','
props = dict(pair.split('=', 1) for pair in lexer)


8 commentaires

Une meilleure stratégie pourrait être comme celle-ci: première division par des signes égaux puis divisée par la dernière virgule de chaque chaîne.


Le moyen le plus simple de le faire est d'utiliser une regex.


@ Seçkinsavaşçı: Sauf s'il ya des signes égaux entre guillemets ...


Si vous insistez sur l'utilisation d'un REGEXP et que des devis doubles seront toujours utilisés pour les chaînes et n'apparaissent jamais dans elles, vous pouvez diviser la chaîne sur " afin que vous puissiez identifier les chaînes citées et les contourner .


@Scotthunter est tout à fait d'accord avec vous, c'est pourquoi ma suggestion n'est pas qualifiée de réponse valide.


Le poste sur la scission sur les semi-couches ne semble pas aborder le type de citation que j'ai.


@Scotthunter Je pense que le module csv pourrait être utilisé avec un fichier = délimiter, et cela va protéger contre le scission sur = dans les guillemets.


@Alex Martelli: C'est pas un duplicata. L'adaptation de la réponse acceptée de La question liée ne fonctionne pas pour l'entrée du courant Question: [suivant (csv.reader ([élément], délimiter = '=')) Pour l'élément dans Suivant (CSV.Reader ([S]))] "jeux, lecture" ). Les questions sont similaires, mais cette question nécessite un tel soutien.


5 Réponses :


1
votes

OK, je pensais en fait une jolie façon de rien, qui est à scinder à la fois sur des virgules et un signe égal, puis prendre 2 jetons à la fois.

{
  'age': '12',
  'name': 'bob',
  'hobbies': 'games,reading',
  'phrase': "I'm cool!",
}


3 commentaires

Il gère également clé = "valeur = valeur"


Ne gère pas les cordes cotées simples.


Si vous avez name = "bob" plutôt que nom = bob , vous pouvez également lire avec exécuté () .



5
votes

Il est possible de faire avec une expression régulière. Dans ce cas, il pourrait en réalité être la meilleure option. Je pense que cela fonctionnera avec la plupart des entrées, même échappé aux citations telles que celle-ci: phrase = 'i \' m cool ' code>

avec le drapeau verbeux, il est possible de faire des expressions régulières compliquées assez lisibles . P>

import re
text = '''age=12,name=bob,hobbies="games,reading",phrase="I'm cool!"'''
regex = re.compile(
    r'''
        (?P<key>\w+)=      # Key consists of only alphanumerics
        (?P<quote>["']?)   # Optional quote character.
        (?P<value>.*?)     # Value is a non greedy match
        (?P=quote)         # Closing quote equals the first.
        ($|,)              # Entry ends with comma or end of string
    ''',
    re.VERBOSE
    )

d = {match.group('key'): match.group('value') for match in regex.finditer(text)}

print(d)  # {'name': 'bob', 'phrase': "I'm cool!", 'age': '12', 'hobbies': 'games,reading'}


4 commentaires

Joli! Ceci est beaucoup plus propre et plus compréhensible que d'utiliser shlex .


Oui. Si cela correspond à une entrée réelle possible, alors évanouis devraient être désinfectés d'une manière ou d'une autre avant d'exécuter la recherche Regex. Un autre exemple, qui est presque plausible est: phrase = "Je suis \" cool \ ", je pense ..."


@ Håkenlid: Je n'enstricultions que cela parce que vous l'avez mentionné dans votre réponse. OP n'a rien dit sur l'échappement de citations à l'intérieur des chaînes. BTW, Solution FSM à partir de ma réponse fonctionne dans ce cas


C'est quelque chose qui pourrait être pertinent, en fonction du type d'intrant à attendre.



0
votes

Python semble offrir de nombreuses façons de résoudre la tâche. Voici un peu plus C tel que la voie mise en œuvre, traiter chaque caractère. Serait intéressant de connaître des temps d'exécution différents.

str = 'age=12,name=bob,hobbies="games,reading",phrase="I\'m cool!"'
key = ""
val = ""
dict = {}
parse_string = False
parse_key = True
# parse_val = False
for c in str:
    print(c)
    if c == '"' and not parse_string:
        parse_string = True
        continue
    elif c == '"' and parse_string:
        parse_string = False
        continue
    if parse_string:
        val += c
        continue
    if c == ',': # terminate entry
        dict[key] = val #add to dict
        key = ""
        val = ""
        parse_key = True
        continue
    elif c == '=' and parse_key:
        parse_key = False
    elif parse_key:
        key += c
    else:
        val+=c
dict[key] = val
print(dict.items())
# {'phrase': "I'm cool!", 'age': '12', 'name': 'bob', 'hobbies': 'games,reading'}


1 commentaires

MH J'ai des résultats corrects, peut-être que cela ne ressemble pas à Python, mais pourquoi le bowvote?



3
votes

Vous pouvez abuser du tokenizer Python pour analyser la liste de valeurs de clé: xxx pré>

sortie h3> xxx pré>

Vous pouvez utiliser un Machine à états fini (FSM) pour mettre en œuvre un analyseur plus strict. L'analyseur utilise uniquement l'état actuel et le jeton suivant pour analyser l'entrée: P>

#!/usr/bin/env python
from tokenize import generate_tokens, NAME, NUMBER, OP, STRING, ENDMARKER

def parse_key_value_list(text):
    def check(condition):
        if not condition:
            raise ValueError((state, token))

    KEY, EQ, VALUE, SEP = range(4)
    state = KEY
    for token in generate_tokens(lambda it=iter([text]): next(it)):
        type, string = token[:2]
        if state == KEY:
            check(type == NAME)
            key = string
            state = EQ
        elif state == EQ:
            check(type == OP and string == '=')
            state = VALUE
        elif state == VALUE:
            check(type in {NAME, NUMBER, STRING})
            value = {
                NAME: lambda x: x,
                NUMBER: int,
                STRING: lambda x: x[1:-1]
            }[type](string)
            state = SEP
        elif state == SEP:
            check(type == OP and string == ',' or type == ENDMARKER)
            yield key, value
            state = KEY

text = '''age=12,name=bob,hobbies="games,reading",phrase="I'm cool!"'''
print(dict(parse_key_value_list(text)))


0 commentaires

11
votes

Vous devez simplement utiliser votre shlex lexer en mode POSIX.

Ajouter posix = true lors de la création du LXER.

(voir Les règles de analyse SHLEX ) xxx

sorties: xxx

ps: expressions régulières ne sera pas en mesure d'analyser les paires de la valeur clé aussi longue Comme l'entrée peut contenir citer = ou , caractères. Même le prétraitement de la chaîne ne serait pas en mesure de faire une analyse d'une expression régulière, car ce type d'entrée ne peut pas être formellement défini comme une langue régulière.


1 commentaires

Je pense que celui-ci est le meilleur, car il gère tous les cas et utilise la bibliothèque shlex au lieu de réinventer la roue.