Python: cherche une chaîne de caractère

Résolu

Bonjour,

Je lis des codes data matrix dont les composants sont spécifiés par des préfixes sous la forme '(XX)'

Exemple: (10)215(17)270109 ou (10)215(96)A(L(11)04122024

Je récupère cette chaîne de caractère en input, et je souhaite en extraire les différentes composantes. Je sais trouver le préfixe (10) et sa position dans la chaîne, mais je ne sais pas trouver la position du préfixe suivant.

Je pourrais chercher uniquement le caractère '(', mais je crains qu'il n'apparaisse dans la donnée (par exemple: A)L)

Je voudrais donc chercher la prochaine occurrence de '(**)', les 2 étoiles pouvant être n'importe quel chiffre...

Comment faire en python?

9 réponses

  1. Modérateur

    Bonjour,

    Réponse courte

    En croisant les idées de (#3) et PierrotLeFou (#6), on peut écrire plus simplement :

    import re
    
    s = "(10)215(96)A(L(11)04122024"
    print(s)
    
    r = re.compile(r"\(\d+\)")
    pairs = [
        (m.start(), m.end())
        for m in re.finditer(r, s)
    ]
    start = 0
    for (i, j) in pairs:
        outside = s[start:i]
        inside = s[i:j]
        if outside:
            print(f"outside = {outside}")
        print(f"inside = {inside}")
        start = j
    outside = s[j:]
    print(f"outside = {outside}")

    Résultat :

    (10)215(96)A(L(11)04122024
    inside = (10)
    outside = 215
    inside = (96)
    outside = A(L
    inside = (11)

    Réponse détaillée

    Étant donné la boucle que ça donne, ça peut valoir le coup de faire une fonction avec les callbacks adéquates. Le code devient :

    import re
    
    RE_DATA_MATRIX = re.compile(r"\(\d+\)")
    
    class ParseDataMatrixVisitor:
        def on_inside(s, i, j):
            pass
        def on_outside(s, i, j):
            pass
    
    class DefaultParseDataMatrixVisitor(ParseDataMatrixVisitor):
        def inside(self, s: str, i: int, j: int):
            print("Inside", s[i:j])
        def outside(self, s: str, i: int, j: int):
            print("Outside", s[i:j])
    
    def parse_data(s, vis: ParseDataMatrixVisitor = None):
        if vis is None:
            vis = DefaultParseDataMatrixVisitor()
        pairs = [
            (m.start(), m.end())
            for m in re.finditer(RE_DATA_MATRIX, s)
        ]
        start = 0
        for (i, j) in pairs:
            if start < i:
                vis.outside(s, start, i)
            vis.inside(s, i, j)
            start = j
        vis.outside(s, j, None)
    
    if __name__ == "__main__":
        s = "(10)215(96)A(L(11)04122024"
        print(s)
        vis = DefaultParseDataMatrixVisitor()
        parse_data(s, vis)

    Réponse finale

    À présent, tout est prêt pour faire ce qu'on veut de ces données. Si par exemple, on veut reconstituer un dictionnaire on écrit juste le visiteur adéquat, voici ce qu'on peut écrire :

    import re
    
    RE_DATA_MATRIX = re.compile(r"\(\d+\)")
    
    class ParseDataMatrixVisitor:
        def on_inside(s, i, j):
            pass
        def on_outside(s, i, j):
            pass
    
    class DictParseDataMatrixVisitor(ParseDataMatrixVisitor):
        def __init__(self):
            self.last_key = None
            self.dict = dict()
        def inside(self, s: str, i: int, j: int):
            self.last_key = s[i:j].lstrip("(").rstrip(")")
        def outside(self, s: str, i: int, j: int):
            last_value = s[i:j]
            self.dict[self.last_key] = last_value
    
    def parse_data(s, vis: ParseDataMatrixVisitor = None):
        if vis is None:
            vis = DefaultParseDataMatrixVisitor()
        pairs = [
            (m.start(), m.end())
            for m in re.finditer(RE_DATA_MATRIX, s)
        ]
        start = 0
        for (i, j) in pairs:
            if start < i:
                vis.outside(s, start, i)
            vis.inside(s, i, j)
            start = j
        vis.outside(s, j, None)
    
    if __name__ == "__main__":
        s = "(10)215(96)A(L(11)04122024"
        print(s)
        vis = DictParseDataMatrixVisitor()
        parse_data(s, vis)
        print(vis.dict)

    Résultat :

    (10)215(96)A(L(11)04122024
    {'10': '215', '96': 'A(L', '11': '04122024'}

    Bonne chance

    0
    1. 'lut,

      Sans expressions régulières.

      def find_digits_between_brackets(string):
          i = 0
          values = {}
          while True:
              i = string.find('(', i)
              if i == -1:
                  break
              if string[i+1:i+3].isdigit() and string[i+3] == ')':
                  values[i+1] = int(string[i+1:i+3])
              i += 1
          return values
      
      s = '(10)215(96)A(L(11)04122024'
      #position => nombre
      print(find_digits_between_brackets(s))

      Peu d'intérêt, mais uniquement pour montrer une autre façon de faire ^-^

      0
      1. Certains préfèreront ta méthode, car beaucoup plus lisible qu'une expression régulière.

        Pour le fun en reprenant ton idée du dictionnaire pour y stocker l'indice :

        import re
        
        def find_digits_between_brackets(string):
            pattern = re.compile(r"\((\d{2})\)")
            return {match.start(1): int(match.group(1)) for match in pattern.finditer(string)}
        
        s = "(10)215(96)A(L(11)04122024"
        
        # position => nombre
        print(find_digits_between_brackets(s))
        
        1
    2. @Diablo76:

      On s'est croisé. J'ai corrigé entre  temps.

      0
      1. Oui avec ton exemple, mais si on reprend une des chaines de caractères proposée par le PO : 

        "(10)215(96)A(L(11)04122024"

        ça donne :

        ['(10)', '215', '(96)', 'A', '(L', '(11)', '04122024']
        0
    3. Meilleurs vœux à tous et toutes.

       ton exemple me retourne cette liste :

      [' (10)', '215(17)', '270109']

      l'exemple de  est mieux adapté, en simplifiant un peu le pattern :

      re.findall(r"\((\d{2})\)", s)
      0
      1. Si la chaîne est bien une alternance de nombres entre parenthèses suivi d'un autre nombre:
        ligne = "(10)215(17)270109"
        liste = ligne.replace("(", ",(").replace(")", "),").split(",")

        liste.pop(0)    # enlever la chaîne vide au début

        Les  éléments parenthèsés seraient en indices pairs (0, 2, ...) dans une liste.
        On enlève les parenthèses comme suit:
        element = element.replace("(", "").replace(")", "")

        Il faudra ensuite convertir tous les éléments en nombre si requis.

        0
        1. Bonjour,

          En fait, ça se ferait plutôt comme ceci:

          import re
          
          s = '(10)215(96)A(L(11)04122024'
          pat = "\(([0-9]{2})\)*"
          
          s_digits = re.findall(pat, s)
          for digit in s_digits: print(digit)

          Résultat:

          1
          1. Modérateur

            Oui mais en l'absence de plus de précision, on ne sait pas s'il ne veut récupérer que les données entre parenthèses ou tout le reste pour en faire par exemple, un tableau associatif/dictionnaire.

            0
        2. Bonjour,

          Je ne maitrise pas regex, un coup de main ou un lien vers un bon tuto m'intéresse ;-)

          0
          1. Modérateur

            Salut,

            faire un split avec une regex ?


            0