Expression régulières Vs State Machines

Les expressions régulières sont un outil très pratique, voire même la martingale dans beaucoup de situations. Seulement dans le cas (improbable) où vous deviez reprendre un code un peu comme celui là :

public static boolean isPrime(String number) {
// compiling the regexp
Pattern p = Pattern.compile("/^1?$|^(11+?)1+$/"); 

// creating the matcher
Matcher m = p.matcher(number); 

boolean isPrime = m.find();

return isPrime;
}

Il n’y a que deux solutions vraisemblablement utilisée, la première consistant à forwarder le boulot à quelqu’un de clairement « plus compétent » que vous (genre un stagiaire…), soit à tout ré-ecrire de manière plus « compréhensible » (où en gros vous allez refaire le travail, seulement comme c’est vous qui allez le faire la regexp sera « nécessairement plus claire » que celle de votre prédécesseur…).

Alors je dis NON, ce n’est pas une fatalité… lol

Le problème est simple, les regexp sont souvent difficilement maintenables. Et la solution est encore plus simple, arréter d’en faire pour n’importe quoi, surtout quand elles ne sont pas nécessaires, et où elles compliquent la tâches plus qu’autre chose… Personnellement j’ai fini par être convaincu qu’il est souvent plus simple de faire une simple machine à états, mais ne vous inquiétez pas je vais m’expliquer par un petit exemple…

Certains *******, de la race de ceux qui adorent dire « mais pourtant ça marche dans mon Excel », adorent donner comme source de donnée pour des transferts des fichiers CSV (Comma Separated Values) suivant un format très drole. Le plus ironique j’ai trouvé avait pour habitude de donner des lignes comme ça :

« Ccy Name, For the current date », 28-Sep-2009, 456.75, (248.56), « 134,546,678.15 »

Avec la virgule comme séparateur de colonne, des parenthèses pour représenter des nombres négatifs, et des virgules (toujours !!) comme séparateur des milliers, et un point pour les nombres à virgules… Mais bien sûr comme ça pose parfois des problèmes d’avoir pleins de virgules comme ça, Excel a décidé de mettre des guillemets autour des chiffres à problèmes (donc pas tout le temps).

Alors comment vous parseriez cette ligne avec une Expression régulière ?

Je ne vous dirait pas la regexp que j’ai obtenu pour des trucs pareils…. mais j’attends avec impatience vos commentaires, si le coeur vous en dit 😉

Donc finalement lors du développement du parseur, j’ai finalement conçu une méthode public String trashAnyUnWantedComma(String line) pour gérer ces cas pourris :

public String trashAnyUnWantedComma(String line) {
		StringBuilder result = new StringBuilder();
		boolean commaSeparatedFieldReached = false;

		for (char c : line.toCharArray()) {
			if (c == '"') {
				commaSeparatedFieldReached =
					(commaSeparatedFieldReached)? false : true;

			} else if (commaSeparatedFieldReached) {
				if (c == ',') {
					continue;
				}
			}
			result.append(c);
		}
		return result.toString();
}

ce qui avec la bonne dose de commentaire, permet d’avoir une fonction simple qui fait le travail et qui reste compréhensible.

Voilà, KIS : Keep It Simple

Publicités

2 Commentaires

  1. Un point théorique important.

    En général, quand il s’agit de problèmes de parenthèsage (typiquement les guillemets – ben oui, il peut y avoir des guillemets à l’intérieur des guillemets eux-mêmes, etc), on ne peut pas utiliser d’automates à états finis, parce qu’il faut compter en quelque sorte le nombre de symboles. Ce qu’un A.E.F. ne saurait faire à lui seul. Soit dit en passant, les expressions régulières sont *exactement* les automates à états finis, et sont compilés comme tels dans la machine. (théorème de je sais plus quoi – même pas sûr que ça soit un théorème d’ailleurs)

    Le langage immédiatement supérieur dans la hiérarchie de Chomsky est la grammaire hors-contexte. Et c’est normalement avec ça qu’on s’en sort pour parser des expressions parenthésés. Une grammaire hors-contexte sera compilée en un automate à pile (pushdown automaton: une pile + un automate à états finis non-déterministe essentiellement).

    Partant de là, je ne voudrais pas me mêler de ce qui ne me regarde pas, mais ta fonction est une regexp déguisée qui ne marche que si l’on n’a qu’un seul niveau de « guillemetisation », et qu’elle ne parse pas vraiment ton entrée non plus…

    Les regexp peuvent être claires et puissantes, pour peu qu’on utilise les fonctions de groupage et qu’on les commente un minimum.

    Bisous!

  2. Tiens en python c’est cadeau (au fait je viens de découvrir ton blog par la même occasion je ne savais même pas que tu en avais un 🙂

    import re
    
    regex = '("[^"]*"|[^, ]*),?'
    line = '"Ccy Name, For the current date", 28-Sep-2009, 456.75, (248.56), "134,546,678.15"'
    parts = [token.replace('"', '') for token in re.findall(regex, line) if token.strip() != '']
    

    Le même en java :

    public static List parseCSV(String line) {
        Pattern p = Pattern.compile("("[^"]*"|[^, ]*),?");
        Matcher m = p.matcher(line);
    
        List result = new ArrayList();
        while(m.find()) {
            String token = m.group();
            if (!"".equals(m.group()))
                result.add(token.endsWith(",") ? token.substring(0, token.length() - 1) : token);
        }
        return result;
    }
    

    Pour l’explication voila comment on procède:
    On cherche n’importe quel caractère différent de  » encadré par deux  » ou n’importe quel caractère différent de , le tout pouvant être suivi par une virgule.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :