Expression régulière pour sélectionner tous les espaces qui ne sont pas entre guillemets?

Je ne suis pas très bon en RegEx, quelqu'un peut-il me donner une regex (à utiliser en Java) qui sélectionnera tous les espaces qui ne sont pas entre deux guillemets? J'essaie de supprimer tous ces espaces d'une chaîne, donc toute solution pour le faire fonctionnera.

Par exemple:

(ceci est un test "phrase pour l'expression rationnelle")

Devrait devenir

(thisisatest "phrase pour l'expression rationnelle")

23
demandé sur Andrew Marshall 2012-03-06 08:33:19

7 réponses

Voici un seul regex-replace qui fonctionne:

\s+(?=([^"]*"[^"]*")*[^"]*$)

, Qui remplacera:

(this is a test "sentence for the regex" foo bar)

Avec:

(thisisatest"sentence for the regex"foobar)

Notez que si les guillemets peuvent être échappés, l'expression rationnelle encore plus verbeuse fera l'affaire:

\s+(?=((\\[\\"]|[^\\"])*"(\\[\\"]|[^\\"])*")*(\\[\\"]|[^\\"])*$)

, Qui remplace l'entrée:

(this is a test "sentence \"for the regex" foo bar)

Avec:

(thisisatest"sentence \"for the regex"foobar)

(notez qu'il fonctionne également avec les espaces arrière échappés: (thisisatest"sentence \\\"for the regex"foobar))

Inutile de dire (?), cela ne devrait vraiment pas être utilisé pour effectuer une telle tâche: Cela fait saigner les yeux, et cela fonctionne sa tâche dans le temps quadratique, alors qu'une solution linéaire simple existe.

Modifier

Une démo rapide:

String text = "(this is a test \"sentence \\\"for the regex\" foo bar)";
String regex = "\\s+(?=((\\\\[\\\\\"]|[^\\\\\"])*\"(\\\\[\\\\\"]|[^\\\\\"])*\")*(\\\\[\\\\\"]|[^\\\\\"])*$)";
System.out.println(text.replaceAll(regex, ""));

// output: (thisisatest"sentence \"for the regex"foobar)
36
répondu Bart Kiers 2012-03-06 13:38:56

Voici la regex qui fonctionne pour les guillemets simples et doubles (en supposant que toutes les chaînes sont délimitées correctement)

\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)

cela ne fonctionnera pas avec les chaînes qui ont des guillemets à l'intérieur.

8
répondu Siva Kranthi Kumar 2014-07-16 10:42:50

Ce n'est tout simplement pas quelque chose que les expressions rationnelles sont bonnes. Les fonctions de recherche et de remplacement avec des expressions rationnelles sont toujours un peu limitées, et toute sorte d'imbrication/confinement devient difficile et/ou impossible.

Je suggère une approche alternative: diviser votre chaîne sur les caractères de citation. Parcourez le tableau de chaînes résultant et supprimez les espaces de chaque autre sous-chaîne (que vous commenciez par la première ou la seconde dépend si vous avez commencé avec une citation ou non). Puis rejoignez-les ensemble, en utilisant des guillemets comme séparateurs. Qui devrait produire les résultats que vous recherchez.

Espérons que cela aide!

PS: notez que cela ne gérera pas les chaînes imbriquées, mais puisque vous ne pouvez pas créer de chaînes imbriquées avec le caractère ASCII double-qutoe, je vais supposer que vous n'avez pas besoin de ce comportement.

PPS: une fois que vous avez affaire à vos sous-chaînes, puis c'est un bon moment pour utiliser des expressions rationnelles pour tuer ces espaces - Pas de guillemets contenant à s'inquiéter. Juste n'oubliez pas d'utiliser le modificateur /.../g pour vous assurer qu'il s'agit d'un remplacement global et pas seulement de la première correspondance.

1
répondu Xavier Holt 2012-03-06 04:56:48

Les groupes d'espaces en dehors des guillemets sont séparés par des éléments qui sont a) pas d'espaces, ou b) à l'intérieur des guillemets.

Peut-être quelque chose comme:

(\s+)([^ "]+|"[^"]*")*

La première partie correspond à une séquence d'espaces; la deuxième partie correspond à des non-espaces (et non-guillemets), ou à des choses entre guillemets, répétées plusieurs fois. La deuxième partie est le séparateur.

Cela vous donnera deux groupes pour chaque élément du résultat; ignorez simplement le deuxième élément. (Nous avons besoin des parenthèses pour precidence plutôt que le regroupement de correspondance là-bas.) Ou, vous pourriez dire, concaténer tous les seconds éléments-bien que vous ayez besoin de faire correspondre le premier mot non-espace, ou dans cet exemple, rendre les espaces facultatifs:

StringBuffer b = new StringBuffer();
Pattern p = Pattern.compile("(\\s+)?([^ \"]+|\"[^\"]*\")*");
Matcher m = p.matcher("this is \"a test\"");
while (m.find()) {
    if (m.group(2) != null)
        b.append(m.group(2));
}
System.out.println(b.toString());

(Je n'ai pas fait beaucoup de regex en Java, alors attendez-vous à des bugs.)

Enfin, c'est comme ça que je le ferais si les expressions rationnelles étaient obligatoires. ;-)

En plus de la technique de Xavier, vous pouvez simplement le faire comme vous le feriez en C: il suffit d'itérer sur les caractères d'entrée, et de copier chacun dans la nouvelle chaîne si elle n'est pas un espace, ou si vous avez compté un nombre impair de guillemets jusqu'à ce point.

1
répondu Edmund 2012-03-06 05:03:38

S'il n'y a qu'un seul jeu de guillemets, vous pouvez le faire:

    String s = "(this is a test \"sentence for the regex\") a b c";

    Matcher matcher = Pattern.compile("^[^\"]+|[^\"]+$").matcher(s);
    while (matcher.find())
    {
        String group = matcher.group();
        s = s.replace(group, group.replaceAll("\\s", ""));
    }

    System.out.println(s); // (thisisatest"sentence for the regex")abc
1
répondu anomal 2012-03-06 13:37:46

Ce n'est pas une solution exacte, mais vous pouvez atteindre votre objectif en procédant comme suit:

Étape 1: Faire correspondre les deux segments

\\(([a-zA-Z ]\*)"([a-zA-Z ]\*)"\\)

Étape 2: supprimer les espaces

temp = $1 replace " " with ""

Étape 3: reconstruire votre chaîne

(temp"$2")
0
répondu Andrew Wei 2016-05-12 13:16:52

Je n'ai absolument aucune idée du fonctionnement de la réponse votée la plus élevée et l'expression rationnelle est énorme, donc je soumets cette réponse un peu plus simple:

\s+(?=(?:'(?:\\'|[^'])+'|[^'])+$)

Cela fonctionne (en théorie) en utilisant une correspondance lookahead pour s'assurer que les guillemets simples ( ' ) sont équilibrés jusqu'à la fin de la chaîne avant de tester si l'espace est un endroit valide à casser.

Cette image la montre en cours d'exécution, et c'est le cas, mais assez lentement. Comme d'autres réponses l'auront probablement noté, en utilisant une telle expression pour diviser une chaîne potentiellement Citée utilise un marteau pour enlever un rivet. Dans mon cas, je saisis cette expression rationnelle dans un programme qui prend une expression rationnelle à diviser (fzf).

0
répondu user1034533 2017-01-18 15:36:30