Tutoriel sur les expressions régulières en Python

Jinku Hu 11 décembre 2023
  1. Expression régulière en Python re.match() Fonction
  2. Expression régulière Python re.search() fonction
  3. Compilation des expressions régulières avec re.complie
  4. Drapeaux dans l’expression régulière Python re module
  5. Vérification des caractères autorisés
  6. Rechercher et remplacer
  7. La fonction findall()
  8. La fonction finditer() est
  9. La fonction split()
  10. Les motifs de base de re sont
  11. Cas de répétition
  12. Répétition non répétitive
  13. Les caractères spéciaux et les séquences dans re
  14. La fonction escape
  15. La fonction group()
Tutoriel sur les expressions régulières en Python

Dans ce tutoriel, vous apprendrez les expressions régulières et les opérations d’expressions régulières définies dans le module re en Python. Le module re est la bibliothèque standard de Python qui supporte les opérations de correspondance des expressions régulières.

Une expression régulière en Python est un ensemble de caractères ou une séquence qui est utilisée pour faire correspondre une chaîne de caractères à un autre motif en utilisant une syntaxe formelle. Vous pouvez penser aux expressions régulières comme un petit langage de programmation qui est intégré à Python.

Vous pouvez utiliser les expressions régulières pour définir des règles et ces règles sont ensuite utilisées pour créer des chaînes de caractères possibles à partir de la chaîne donnée avec laquelle vous voulez faire correspondre le modèle. Les expressions régulières en Python sont interprétées comme un ensemble d’instructions.

Expression régulière en Python re.match() Fonction

Vous pouvez utiliser la fonction match pour faire correspondre le motif RE avec la chaîne donnée. La fonction de correspondance contient des drapeaux. Les drapeaux définissent le comportement d’une expression régulière et peuvent contenir différentes valeurs que vous verrez plus tard dans ce tutoriel.

Voici la syntaxe de la fonction de correspondance en Python:

re.match(pattern, string, flags)

Elle a trois arguments,

  1. pattern est le motif d’expression régulière qui doit être mis en correspondance
  2. La string est la chaîne donnée qui doit être mise en correspondance avec l’expression régulière
  3. Les flags sont utilisés pour changer le comportement des expressions régulières, et ils sont optionnels.

Si la correspondance est effectuée avec succès, l’objet match sera renvoyé, sinon None sera renvoyé. Les objets match ont en plus deux méthodes principales qui sont les fonctions group(num) et group(). Le but principal de ces fonctions est de retourner la correspondance ou une sous-séquence spécifique et toutes les sous-séquences respectivement.

Utilisation de la fonction re.match

L’exemple suivant montre comment vous pouvez utiliser la fonction match:

import re

strTest = "Hello Python Programming"
mobj = re.match(r"hello", strTest, re.I)
print(mobj.group())

Dans ce code, le module re est tout d’abord importé. Ensuite vous allez comparer une chaîne strTest avec le modèle RE et la valeur retournée par la fonction match sera assignée à mobj. La fonction de correspondance est appelée en utilisant re puis, entre parenthèses, le premier argument est le motif à comparer, et ensuite vous aurez la chaîne donnée à partir de laquelle le motif sera comparé et une valeur de drapeau sera aussi passée. Ici, re.I est la valeur du drapeau qui signifie IGNORECASE, donc il sera ignoré si le motif et la chaîne ont des lettres différentes (soit en majuscules, soit en minuscules).

La sortie est:

Hello

Dans cet exemple, le préfixe r est utilisé, ce qui indique que la chaîne est une chaîne brute. Dans une chaîne brute, il n’est pas nécessaire d’écrire des doubles slashes lorsque vous utilisez des séquences d’échappement ; par exemple, si vous voulez un backslash, alors vous avez juste un simple \ mais pas de doubles backslashes \ comme dans les chaînes normales. C’est la seule différence entre une chaîne régulière et une chaîne brute.

En utilisant la fonction re.match avec une chaîne normale

Considérons l’exemple ci-dessous dans lequel une chaîne régulière est utilisée à la place d’une chaîne brute:

import re

str = "\\tHello Python Programming"
mobj = re.match("\\thello", str, re.I)  # no match

str = "\tHello Python Programming"
mobj = re.match("\\thello", str, re.I)  # \thello is matching

Expression régulière Python re.search() fonction

Vous pouvez utiliser la fonction re.search() pour rechercher le motif RE dans la chaîne donnée. La fonction search contient trois arguments dans la fonction : le pattern, donné string, et flags (optionnel) respectivement.

Ce qui suit est la syntaxe de la fonction de recherche en Python:

re.search(pattern, string, flags)

Le code Python suivant démontre l’utilisation de la fonction search():

import re

str = "Hello Python Programming"
sobj = re.search(r"programming", str, re.I)
print(sobj.group())
Programming

Dans ce code, la recherche du mot programming est effectuée. La fonction search recherche dans la chaîne entière. La différence entre search et match est que la fonction match ne vérifie qu’au début de la chaîne alors que la fonction search recherche dans toute la chaîne.

Recherche au début avec re.search

Si vous voulez chercher au début de la chaîne, vous pouvez utiliser ^. Considérons l’exemple suivant:

import re

str = "Hello Python Programming"
sobj = re.search(r"^programming", str, re.I)
print(sobj.group())  # no match is found

sobj = re.search(r"^hello", str, re.I)
print(sobj.group())  # matching: Hello

Ici, ^ effectuera la recherche uniquement au début de la chaîne.

Rechercher à la fin en utilisant re.search

Vous pouvez également rechercher à la fin de la chaîne de caractères donnée. Cela peut être fait en utilisant $ à la fin du motif. Considérez le code ci-dessous:

import re

str = "Hello Python Programming"
sobj = re.search(r"programming$", str, re.I)
print(sobj.group())  # matching: Programming

sobj = re.search(r"hello$", str, re.I)
print(sobj.group())  # no match found

Compilation des expressions régulières avec re.complie

Les expressions régulières en Python, lorsqu’elles sont compilées, sont converties en motifs. Ces motifs sont en fait les objets de motif qui contiennent différentes fonctions pour effectuer différentes tâches qui peuvent inclure la recherche, la correspondance, et le remplacement, etc.

Lorsque vous compilez un motif, vous pouvez utiliser ce motif plus tard dans le programme.

Utilisation de patterns précompilés

Considérons le code ci-dessous dans lequel le motif r"\d" est compilé, ce qui signifie le premier chiffre de la chaîne de caractères et ensuite utilisé ce motif pour appeler la fonction de recherche et passer une chaîne de caractères dans la fonction de recherche. Ce motif sera recherché dans la chaîne de caractères fournie à la fonction de recherche. De même, vous pouvez utiliser ce motif précompilé avec la fonction de recherche comme suit:

import re

compPat = re.compile(r"(\d)")
sobj = compPat.search("Lalalala 123")
print(mobj.group())

mobj = compPat.match("234Lalalala 123456789")
print(mobj.group())
1
2

Drapeaux dans l’expression régulière Python re module

Vous pouvez utiliser Flags are pour modifier le comportement d’une expression régulière. Dans une fonction, les drapeaux sont optionnels. Vous pouvez utiliser les drapeaux de deux manières différentes, soit en utilisant le mot-clé flags et en lui affectant une valeur de drapeau, soit en écrivant directement la valeur du drapeau. Vous pouvez avoir plus d’une valeur de drapeau dans le RE littéral; ceci peut être fait en utilisant l’opérateur bit-système OR |.

Considérez le tableau suivant dans lequel certains des drapeaux couramment utilisés sont décrits avec des littéraux d’Expression Régulière:

Valeur du drapeau Description
re.I Ce modificateur va ignorer le cas des chaînes et des motifs lors de la correspondance.
re.L Ce modificateur est utilisé pour interpréter les mots par rapport à la locale courante.
re.M Ce modificateur est utilisé pour que $ corresponde à la fin de la ligne et non à la fin de la chaîne. De même, ^ correspondra au début de la ligne et non au début de la chaîne.
re.S Ce modificateur est utilisé pour faire un point . pour correspondre à n’importe quel caractère. Cela inclut aussi une nouvelle ligne.
re.U Ce modificateur est utilisé pour interpréter les caractères comme un jeu de caractères Unicode.
re.X Ce modificateur est utilisé pour ignorer les espaces. Il fera # comme un marqueur de commentaire.

Utilisation de plusieurs valeurs de drapeau

Considérez le code Python suivant dans lequel vous verrez comment utiliser des valeurs de drapeau multiples pour modifier le comportement de RE. Les valeurs de drapeaux multiples peuvent être incluses par l’opérateur OR (|) au niveau du bit:

import re

s = re.search("L", "Hello")
print(s)  # Output: None, L is there but in small letter and we didn't use flags

s = re.search("L", "Hello", re.I)
print(s)  # Output: 1

s = re.search("L", "^Hello", re.I | re.M)
print(s)  # Output: 1, searching will be made from the start of line and case is ignored

Vérification des caractères autorisés

Vous pouvez également vérifier si une certaine chaîne de caractères contient ou non une certaine plage de caractères.

Définition d’une fonction et vérification des caractères autorisés

Considérons l’exemple suivant dans lequel une fonction est définie et utilise également un modèle précompilé pour vérifier si les certains caractères sont dans la chaîne passée ou non:

import re


def check(str):
    s = re.compile(r"[^A-Z]")
    str = s.search(str)
    return not bool(str)


print(check("HELLOPYTHON"))  # Output: True
print(check("hellopython"))  # Output: False

Dans cette fonction, un motif qui est r '[^A-Z]' est compilé et utilisé pour rechercher dans une chaîne de caractères passée lorsque cette fonction nommée check est appelée. Cette fonction vérifie en fait si la chaîne passée contient les lettres A-Z(majuscules) ou non. De même, on peut voir que lorsque vous passez une chaîne en lettres minuscules, false est renvoyé.

Rechercher et remplacer

Le module re fournit une fonction qui est une fonction sub qui est utilisée pour remplacer toutes les occurrences du pattern dans la string donnée en utilisant l’attribut repl dans la fonction. Les caractères seront remplacés jusqu’à ce que le nombre count soit atteint. La fonction sub retournera la chaîne mise à jour.

Ce qui suit est la syntaxe de la sous-fonction:

re.sub(pattern, repl, string, count=0)

Utilisation de la fonction sub

Considérons l’exemple ci-dessous dans lequel la fonction sub remplace la chaîne entière par une chaîne donnée:

import re

s = "Playing 4 hours a day"
obj = re.sub(r"^.*$", "Working", s)
print(obj)
Working

Ici, la fonction sub est utilisée. Le motif r'^.*$ signifie que l’on commence par le début de la chaîne et ensuite .* signifie ce qui se trouve dans la chaîne jusqu’à la fin $ de la chaîne. L’argument Working remplacera alors la chaîne entière s.

En utilisant la fonction sub pour effacer tous les chiffres d’une chaîne

Considérons l’exemple suivant dans lequel la fonction sub supprime les chiffres de la chaîne de caractères donnée. Pour cela, vous pouvez utiliser \d:

import re

s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r"\d", "", s)
print(obj)
Working   five   hours   a  day

De même, vous pouvez effacer les caractères de la chaîne. Pour cela, vous pouvez utiliser \D.

import re

s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r"\D", "", s)
print(obj)
76823437892346563244646345

La fonction findall()

La fonction findall retourne une liste de toutes les chaînes de caractères correspondant au motif. La différence entre la fonction search et la fonction findall est que findall trouve toutes les correspondances alors que search ne trouve que la première correspondance. Cette fonction trouve les correspondances qui ne se chevauchent pas et les retourne sous forme de liste de chaînes de caractères.

La syntaxe de la fonction findall est la suivante

findall(pattern, string, flags)

Ici, pattern est le modèle RE que vous trouverez dans la chaîne donnée avec quelques valeurs de flags par exemple re.I pour ignorer la casse.

Trouver toutes les correspondances qui ne se chevauchent pas

Dans l’exemple suivant, findall trouve des correspondances non chevauchantes:

import re

str = "Working 6 hours a day. Studying 4 hours a day."
mobj = re.findall(r"[0-9]", str)
print(mobj)
["6", "4"]

r'[0-9]' is a pattern finding all the digits in the given string and a list of strings is returned (no matter they are digits) which is stored in mobj.

findall avec les fichiers

Vous pouvez aussi utiliser findall pour trouver dans un fichier. Quand vous utilisez findall avec un fichier, il retournera une liste de toutes les chaînes de caractères correspondantes dans le fichier. Comme la fonction read() du fichier sera utilisée, vous n’aurez pas à itérer à travers chaque ligne du fichier en utilisant une boucle car elle retournera le texte entier du fichier sous forme de chaîne. Considérons l’exemple suivant:

import re

file = open("asd.txt", "r")
mobj = re.findall(r"arg.", file.read())
print(mobj)
file.close()
["arg,", "arg,", "arg,", "argv", "argv", "argv"]

Dans cet exemple, le fichier est ouvert en premier en mode lecture. Le motif r'arg.' est mis en correspondance avec le contenu du fichier et vous avez la liste des chaînes de caractères correspondantes dans la sortie.

La fonction finditer() est

La fonction finditer peut être utilisée pour trouver le motif RE dans les chaînes de caractères ainsi que l’emplacement des chaînes de caractères correspondantes qui est l’index des chaînes de caractères. Cette fonction itére en fait les chaînes correspondantes et retourne les index ou les emplacements de la chaîne.

Voici la syntaxe de la fonction finditer:

finditer(pattern, string, flags)

Itération sur les correspondances

La seule différence entre findall et finditer est que finditer retourne l’index ainsi que les chaînes de caractères correspondantes. Dans le code ci-dessous, finditer est utilisé pour trouver les emplacements des chaînes de caractères correspondantes tout en itérant sur les correspondances (chaînes de caractères correspondantes) en utilisant pour la boucle.

import re

str = "Working 6 hours a day. Studying 4 hours a day."
pat = r"[0-9]"
for mobj in re.finditer(pat, str):
    s = mobj.start()
    e = mobj.end()
    g = mobj.group()
    print("{} found at location [{},{}]".format(g, s, e))
6 found at location [8,9]
4 found at location [32,33]

Dans cet exemple, le motif est les chiffres de 0 à 9 que l’on trouve dans str. La boucle for est itérative sur les chaînes correspondantes retournées par finditer. Dans la boucle, les fonctions start, end et group retournent respectivement l’index de début, l’index de fin et la correspondance trouvée dans chaque itération de la chaîne renvoyée par finditer.

La fonction split()

La fonction split est utilisée pour diviser une chaîne de caractères.

La syntaxe de la fonction split est la suivante

split(patter, string, maxsplit, flags)

Ici, max est le nombre total de fractionnements de chaîne de caractères. Si au maximum des fractionnements maxsplit se produisent, le reste de la chaîne est renvoyé comme élément final de la liste. La valeur par défaut de max est 0 ce qui signifie un nombre illimité de fractionnements.

Fractionnement d’une chaîne de caractères

La fonction split retourne chaque mot d’une chaîne

Dans le code ci-dessous, une chaîne de caractères est divisée selon le modèle donné et le nombre de divisions maximum.

import re

str = "Birds fly high in the sky for ever"
mobj = re.split("\s+", str, 5)
print(mobj)
["Birds", "fly", "high", "in", "the", "sky for ever"]

Dans cet exemple, le caractère du motif \s est un caractère spécial qui correspond au caractère espace, ce qui est équivalent à [ \t\n\r\f\v]. Par conséquent, vous pourriez avoir des mots séparés. La valeur de max est ici 5, ce qui fait des divisions 6, et le dernier élément est le reste de la chaîne après la 5ème division.

Les motifs de base de re sont

Les expressions régulières peuvent spécifier des motifs qui sont comparés à des chaînes de caractères données. Voici les motifs de base des expressions régulières:

Motif Description
^ Il est utilisé pour correspondre au début de la chaîne.
$ Ce motif correspondra à la fin de la chaîne de caractères.
. Le point est utilisé pour correspondre à un caractère (le saut de ligne n’est pas inclus).
[...] Il est utilisé pour faire correspondre un seul caractère entre parenthèses.
[^...] Cela correspondra à un seul caractère mais pas entre parenthèses.
* 0 ou plusieurs occurrences du re précédent dans une chaîne donnée.
+ 1 ou plusieurs occurrences du re précédent dans une chaîne donnée.
? 0 ou 1 occurrences du re précédent dans une chaîne donnée.
{n} Il correspondra à n occurrences dans une chaîne donnée.
{n,} Il correspondra à n ou plus de n occurrences.
{n,m} Ce motif est utilisé pour faire correspondre au moins n et au plus m correspondances dans la chaîne.
`a b`
(re) Ce modèle est utilisé pour regrouper les expressions régulières et il se souviendra du texte correspondant.
(?imx) Il basculera temporairement sur i ou m ou x dans RE. Lorsque vous utilisez des parenthèses, seule la zone entre parenthèses est affectée.
(?-imx) Il désactive temporairement i ou m ou x dans RE. Lorsque vous utilisez des parenthèses, seule la zone entre parenthèses est affectée.
(?: re) Ce modèle est utilisé pour regrouper les expressions régulières mais il ne se souviendra pas du texte correspondant.
(?imx: re) Il basculera temporairement sur i ou m ou x dans RE entre parenthèses.
(?-imx: re) Il bascule temporairement i ou m ou x dans RE entre parenthèses.
(?#...) C’est un commentaire.
(?= re) Il est utilisé pour spécifier la position en utilisant un motif. Il n’a pas de plage.
( ?! re) Il est utilisé pour spécifier la position en utilisant une négation de motif. Il n’a pas de plage.
(?> re) Ce modèle est utilisé pour faire correspondre des modèles indépendants.
\w Ce modèle est utilisé pour faire correspondre les mots.
\W Ce modèle est utilisé pour faire correspondre les non mots.
\s Il correspondra aux espaces blancs. \est égal à la distance.
\S Il correspondra aux espaces non blancs.
\d égal à [0-9]. Il correspond aux chiffres de la chaîne de caractères.
\D Il correspond à des non chiffres.
\A correspondent au début de la chaîne.
\Z correspondent au bout de la ficelle. Et s’il y a une nouvelle ligne, elle correspondra avant la nouvelle ligne.
\G au point où le dernier match a été terminé.
\b correspond aux limites des mots lorsqu’il est à l’extérieur des parenthèses mais lorsqu’il est à l’intérieur des parenthèses, il correspond à l’espacement arrière.
\B correspondent à des limites non verbales.
n, t, etc. \Le mot n est utilisé pour les nouvelles lignes, t correspond à tab et ainsi de suite.
\1...\9 Ce motif correspondra à la nième sous-expression (groupée).
\10 \10 correspond généralement à la nième sous-expression (groupée) si la correspondance est déjà faite. Si la correspondance n’est pas déjà faite, \10 fournira la représentation octale d’un code de caractère.

Cas de répétition

Le tableau suivant montre quelques exemples de cas de répétition avec description:

Exemples Descriptions
ab? Il correspondra soit à a, soit à ab.
ab* ab* correspondra à ab et a et tout a suivi de tout b.
ab+ ab+ signifie que a est suivi de b et non seulement a. a doit être suivi d’un b non nul.
\d{2} Il correspondra exactement à 2 chiffres.
\d{2,} Il correspondra à 2 chiffres ou plus.
\d{2,4} Il correspondra aux chiffres 2, 3 et 4.

Répétition non répétitive

Dans les expressions régulières, la répétition est par défaut gourmande qui essaie de correspondre au plus grand nombre de répétitions possible.

Les qualificatifs tels que *, + et ? sont des qualificatifs avides. Lorsque vous utilisez .*, il effectuera une correspondance gourmande et correspondra à la chaîne entière, ce qui aura pour résultat de faire correspondre autant de caractères que possible. Considérez le code ci-dessous:

import re

mobj = re.match(r".*", "Birds fly high in sky")
print(mobj.group())
Birds fly high in the sky

Vous pouvez donc voir ici que la chaîne entière est appariée.

Quand vous ajoutez ? avec .+, vous aurez un re non gourmand et le motif .+? correspondra au moins de caractères possible dans la chaîne.

import re

mobj = re.match(r".*", "Birds fly high in sky")
print(mobj.group())

Le résultat est le premier caractère de la chaîne

B

Les caractères spéciaux et les séquences dans re

Les caractères spéciaux dans re commencent par un \. Par exemple, nous avons un \A qui correspondra au début de la chaîne de caractères.

Ces caractères spéciaux sont décrits dans le tableau ci-dessus.

Dans cette section, vous trouverez des exemples de certains de ces caractères spéciaux:

import re

str = "Birds fly high in the sky"
# \A
# OUTPUT: B, here \A will match at beginning only.
mobj = re.match(r"\Ab", str, re.I)

# \d
mobj = re.match(r"\d", "4 birds are flying")  # OUTPUT: 4

# \s
mobj = re.split("\s+", "birds fly high in the sky", 1)  # OUTPUT: ['Birds', 'fly']

La fonction escape

La fonction Echappement est utilisée pour échapper tous les caractères de la chaîne. Les lettres ASCII, les chiffres, et les _ ne seront pas échappés. La fonction escape est utilisée quand vous voulez extraire des métacaractères d’une chaîne de caractères.

Voici la syntaxe de la fonction d’échappement:

escape(pattern)

Dans l’exemple suivant, une chaîne de caractères www.python.org est passée à la fonction d’échappement. Dans cet exemple, nous avons . qui est un méta-caractère et il sera extrait ou mis en correspondance:

print(re.escape("www.python.org"))
www\.python\.org

Ici, . est un méta-caractère qui est extrait ou mis en correspondance. Chaque fois qu’un méta-caractère est trouvé en utilisant une fonction d’échappement, vous aurez \ avant le caractère.

Caractères spéciaux d’échappement

Les caractères comme les parenthèses [ and ] ne peuvent pas être comparés. Considérons l’exemple suivant:

import re

mobj = re.search(r"[a]", "[a]b")
print(mobj.group())
a

Ici, vous pouvez voir que les crochets [ and ] ne sont pas compatibles.

Vous pouvez les faire correspondre en utilisant la fonction d’échappement:

import re

mobj = re.search(r"\[a\]", "[a]b")
print(mobj.group())
[a]b

La fonction group()

La fonction group est utilisée pour retourner un ou plusieurs sous-groupes de la correspondance trouvée. La fonction group peut avoir quelques arguments.

Voici la syntaxe de la fonction de groupe:

group(group1, group2, ..., groupN)

Si vous avez un seul argument dans la fonction de groupe, le résultat sera une seule chaîne de caractères mais si vous avez plus d’un argument, alors le résultat sera un tuple (contenant un élément par argument).

Lorsqu’il n’y a pas d’argument, par défaut l’argument sera zéro et il retournera la correspondance entière.

Quand l’argument groupN est zéro, la valeur retournée sera la chaîne de caractères complète.

Lorsque vous spécifiez le numéro de groupe ou l’argument comme une valeur négative ou une valeur plus grande que le nombre de groupes dans le modèle, alors l’exception IndexError se produira.

Considérez le code ci-dessous dans lequel il n’y a pas d’argument dans la fonction group qui soit équivalent au groupe(0).

import re

str = "Working 6 hours a day"
mobj = re.match(r"^.*", str)
print(mobj.group())
Working 6 hours a day

Ici, group() est utilisé et vous avez la chaîne de caractères correspondante entière.

Choisir des parties de textes correspondants

Dans l’exemple suivant, la fonction group est utilisée avec des arguments pour récupérer les groupes correspondants:

import re

a = re.compile("(p(q)r)s")
b = a.match("pqrs")
print(b.group(0))
print(b.group(1))
print(b.group(2))
pqrs
pqr
q

Ici, group(0) retourne la correspondance complète. group(1) renverra la première correspondance qui est pqr et group(2) renverra la seconde correspondance qui est q.

Groupes nommés

En utilisant les groupes nommés, vous pouvez créer un groupe de capture. Ce groupe peut alors être désigné par son nom. Considérez l’exemple ci-dessous:

import re

mobj = re.search(r"Hi (?P<name>\w+)", "Hi Roger")
print(mobj.group("name"))
Roger

Les groupes non capturables

Un groupe non capturant peut être créé en utilisant ?:. Le groupe non capturant est utilisé quand vous ne voulez pas le contenu du groupe.

import re

mobj = re.match("(?:[pqr])+", "pqr")
print(mobj.groups())
()
Auteur: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook