Voir le flux RSS

tyrtamos

[Actualité] [Python] Ajouter des recherches en expressions régulières à sqlite3

Noter ce billet
par , 04/03/2015 à 11h29 (1048 Affichages)
Problématique

Le SGBDR sqlite3 qui vient avec Python ne comporte pas d'instruction permettant les recherches avec des expressions régulières. Il y a cependant des cas où on en a vraiment besoin. Comme c'est facile à ajouter, on va le faire!

Solution

On va ajouter 2 fonctions:
  • regexp(motif, item) qui va dire si oui on non l'item satisfait le motif
  • regextract(motif, item) qui va retourner la 1ère sous-chaine trouvée qui satisfait le motif, ou None s'il n'y en a pas.


Fonction regexp

Voilà la 1ère fonction. Il faut, bien sûr, importer le module re avant (=> import re):

Code PYTHON : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
def regexp(motif, item):
    """retourne True si le motif regex a été satisfait dans l'item
       False sinon
    """
    regex = re.compile(motif, re.I)  # re.I: ignore casse
    return regex.search(item) is not None

On déclare cette fonction dans le pilote sqlite3 de Python comme suit (cnx est la variable de connexion à la base de données):

Code PYTHON : Sélectionner tout - Visualiser dans une fenêtre à part
cnx.create_function("REGEXP", 2, regexp)

Cette fonction, bien que non fournie par sqlite3, a cependant prévu une syntaxe particulière. Ainsi, au lieu d'utiliser une syntaxe comme "REGEXP(motif,item)", on va utiliser: "item REGEXP motif"!

Voilà un petit test:

Une table appelée "test" contient un champ "mots" qui contient:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
aaa bbb ccc(ddd)eee fff
aaa (bbb) ccc
(aaa)bbb ccc ddd
aaabbbcccddd(eee)
aaabbbcccddd
On va chercher avec un script SQL les lignes qui contiennent des parenthèses ("cur" est ici un curseur):

Code PYTHON : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
cur.execute("""
    SELECT mots
    FROM test
    WHERE mots REGEXP ?
    ORDER BY mots
    """, ('\(.*\)',))
liste = cur.fetchall()

Voilà ce que liste contient:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
(aaa)bbb ccc ddd
aaa (bbb) ccc
aaa bbb ccc(ddd)eee fff
aaabbbcccddd(eee)
On voit bien que l'item qui ne contient pas les parenthèses n'a pas été pris en compte.

Fonction regextract

Dans la liste précente qui contient au moins une fois des parenthèses, on va créer une 2ème fonction qui va en extraire son contenu.

Code PYTHON : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
def regextract(motif, item):
    """retourne la 1ère sous-chaine qui satisfait au motif regex donné
       si aucune sous-chaine n'est trouvée, renvoie None
    """
    regex = re.compile(motif, re.I)  # re.I: ignore la casse
    result = regex.findall(item)  # liste des sous-chaines trouvées
    if result != []:
        return result[0]  # on ne renvoie que la 1ère s'il y en a plusieurs
    else:
        return None

Application:

Code PYTHON : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
cur.execute("""
    SELECT regextract(?,mots) as parentheses
    FROM test
    WHERE mots REGEXP ?
    ORDER BY parentheses
    """, ('\(.*\)', '\(.*\)'))
liste = cur.fetchall()

Et cette fois le résultat sera:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
(aaa)
(bbb)
(ddd)
(eee)
Bien sûr, on pourrait ne prendre que le contenu des parenthèses sans celles-ci: il suffit de modifier un peu le motif:

.

Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Viadeo Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Twitter Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Google Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Facebook Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Digg Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Delicious Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog MySpace Envoyer le billet « [Python] Ajouter des recherches en expressions régulières à sqlite3 » dans le blog Yahoo

Mis à jour 05/03/2015 à 07h39 par tyrtamos (petites corrections "cosmétiques")

Catégories
Python , Programmation , Python

Commentaires

  1. Avatar de parice02
    • |
    • permalink
    Bonsoir.
    Je voudrais savoir si la fonction regexp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    def regexp(motif, item): 
        """retourne True si le motif regex a été satisfait dans l'item 
           False sinon 
        """ 
        regex = re.compile(motif, re.I)  # re.I: ignore casse 
        return regex.search(item) is not None
    est appelé à chaque comparaison avec les mots de la base de données lors de cette exécution:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    cur.execute(""" 
        SELECT mots 
        FROM test 
        WHERE mots REGEXP ? 
        ORDER BY mots 
        """, ('\(.*\)',)) 
    liste = cur.fetchall()
    .
    Si oui, est-que la compilation est refaite à chaque fois.
  2. Avatar de tyrtamos
    • |
    • permalink
    Bonjour,

    Bien vu! Effectivement, il est logique de penser que cette fonction est appelée à chaque ligne, et donc que la compilation du motif regex est inutile!

    Je viens d'ailleurs d'essayer, et on trouve bien le même résultat.

    La suppression de la compilation est facile à faire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    def regexp(motif, item):
        """retourne True si le motif regex a été satisfait dans l'item
           False sinon
        """
        return re.search(motif, item, re.I) is not None
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def regextract(motif, item):
        """retourne la 1ère sous-chaine de item qui satisfait le motif regex donné
           si aucune sous-chaine n'est trouvée, retourne None
        """
        result = re.findall(motif, item, re.I)  # liste des sous-chaines trouvées
        if result != []:
            return result[0]  # on ne renvoie que la 1ère s'il y en a plusieurs
        else:
            return None
    Merci!