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. 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. 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.

  2. Bonjour,

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

    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.

      1. 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)
        1. @Diablo76:

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

          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']
        2. '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 ^-^

          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))
            
        3. 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