J'ai une chaîne multiligne qui est délimitée par un ensemble de délimiteurs différents:
(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)
Je peux diviser cette chaîne en ses parties en utilisant String.split
, mais il semble que je ne puisse pas obtenir la chaîne réelle, qui correspondait à l'expression rationnelle du délimiteur.
En d'autres termes, voici ce que j'obtiens:
Text1
Text2
Text3
Text4
C'est ce que je veux
Text1
DelimiterA
Text2
DelimiterC
Text3
DelimiterB
Text4
Existe-t-il un moyen JDK de diviser la chaîne à l'aide d'une expression régulière de délimiteur, mais également de conserver les délimiteurs?
Réponses:
Vous pouvez utiliser Lookahead et Lookbehind. Comme ça:
Et vous obtiendrez:
Le dernier est ce que vous voulez.
((?<=;)|(?=;))
équivaut à sélectionner un caractère vide avant;
ou après;
.J'espère que cela t'aides.
EDIT Les commentaires de Fabian Steeg sur la lisibilité sont valides. La lisibilité est toujours le problème pour RegEx. Une chose que je fais pour faciliter cela est de créer une variable dont le nom représente ce que fait l'expression régulière et utilise le format Java String pour aider cela. Comme ça:
Cela aide un peu. :-RÉ
la source
split(";", true)
serait tellement plus lisible quesplit("((?<=;)|(?=;))")
.String.format(WITH_DELIMITER, ";");
car le format est une méthode statique.[\\s,]+
) que vous voulez faire correspondre complètement. Les expressions rationnelles requises deviennent encore plus longues, car vous avez besoin d'un regard négatif supplémentaire {devant, derrière} pour éviter de les faire correspondre au milieu, par exemple.(?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
.Vous souhaitez utiliser des contournements et fractionner sur des correspondances de largeur nulle. Voici quelques exemples:
Et oui, c'est là une affirmation triplement imbriquée dans le dernier schéma.
Questions connexes
Voir également
la source
Une solution très naïve, qui n'implique pas l'expression régulière, serait d'effectuer un remplacement de chaîne sur votre délimiteur dans le sens (en supposant une virgule pour le délimiteur):
Où vous pouvez remplacer tilda (~) par un délimiteur unique approprié.
Ensuite, si vous divisez votre nouveau délimiteur, je pense que vous obtiendrez le résultat souhaité.
la source
Je n'aime pas vraiment l'autre façon, où vous obtenez un élément vide devant et derrière. Un délimiteur n'est généralement pas au début ou à la fin de la chaîne, donc vous finissez le plus souvent par perdre deux bons emplacements de tableau.
Modifier: cas limites fixes. La source commentée avec des cas de test peut être trouvée ici: http://snippets.dzone.com/posts/show/6453
la source
null
argument est la bonne façon de procéder. Une manipulation silencieuse entraîne des erreurs apparaissant plus tard.Je suis arrivé en retard, mais pour revenir à la question d'origine, pourquoi ne pas simplement utiliser des contournements?
production:
EDIT: Ce que vous voyez ci-dessus est ce qui apparaît sur la ligne de commande lorsque j'exécute ce code, mais je vois maintenant que c'est un peu déroutant. Il est difficile de savoir quelles virgules font partie du résultat et lesquelles ont été ajoutées par
Arrays.toString()
. La mise en évidence de la syntaxe de SO n'aide pas non plus. Dans l'espoir que la mise en évidence fonctionne avec moi plutôt que contre moi, voici à quoi ressembleraient ces tableaux, je les déclarais dans le code source:J'espère que c'est plus facile à lire. Merci pour l'avertissement, @finnw.
la source
Je sais que c'est une question très très ancienne et la réponse a également été acceptée. Mais je voudrais quand même soumettre une réponse très simple à la question d'origine. Considérez ce code:
PRODUCTION:
J'utilise juste la limite de mot
\b
pour délimiter les mots sauf quand c'est le début du texte.la source
abcdef
avecde
comme délimiteur, mais vous pouvez résoudre le problème en utilisant(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
(?!^|$)
J'ai jeté un œil aux réponses ci-dessus et honnêtement, je ne trouve aucune réponse satisfaisante. Ce que vous voulez faire, c'est essentiellement imiter la fonctionnalité de partage Perl. Pourquoi Java ne permet pas cela et a une méthode join () quelque part me dépasse mais je m'éloigne du sujet. Vous n'avez même pas vraiment besoin d'un cours pour ça. C'est juste une fonction. Exécutez cet exemple de programme:
Certaines des réponses précédentes ont un contrôle nul excessif, auquel j'ai récemment écrit une réponse à une question ici:
https://stackoverflow.com/users/18393/cletus
Quoi qu'il en soit, le code:
la source
J'aime l'idée de StringTokenizer car elle est énumérable.
Mais il est également obsolète et remplacé par String.split qui retourne un String [] ennuyeux (et n'inclut pas les délimiteurs).
J'ai donc implémenté un StringTokenizerEx qui est un Iterable, et qui prend une vraie expression rationnelle pour diviser une chaîne.
Une expression rationnelle vraie signifie qu'il ne s'agit pas d'une 'séquence de caractères' répétée pour former le délimiteur:
'o' ne correspondra qu'à 'o' et divisera 'ooo' en trois délimiteurs, avec deux chaînes vides à l'intérieur:
Mais l'expression régulière o + renverra le résultat attendu lors de la division de "aooob"
Pour utiliser ce StringTokenizerEx:
Le code de cette classe est disponible sur DZone Snippets .
Comme d'habitude pour une réponse de défi de code (une classe autonome avec cas de test inclus), copiez-collez-la (dans un répertoire 'src / test') et exécutez-la . Sa méthode main () illustre les différents usages.
Remarque: (édition fin 2009)
L'article Réflexions finales: Java Puzzler: Splitting Hairs explique bien le comportement bizarre de
String.split()
.Josh Bloch a même commenté en réponse à cet article:
La bibliothèque commune Google Guava contient également un Splitter qui est:
Il peut donc être utile de vérifier. D'après leur documentation initiale (pdf) :
la source
Passez le 3ème aurgument comme "vrai". Il renverra également les délimiteurs.
la source
Voici une implémentation simple et propre qui est cohérente
Pattern#split
et fonctionne avec des modèles de longueur variable, qui ne peuvent pas être pris en charge par l'arrière, et elle est plus facile à utiliser. Elle est similaire à la solution fournie par @cletus.Je ne fais pas de vérifications nulles ici,
Pattern#split
n'est-ce pas, pourquoi devrais-je le faire. Je n'aime pas leif
à la fin mais c'est nécessaire pour la cohérence avec lePattern#split
. Sinon, je voudrais ajouter inconditionnellement, ce qui entraîne une chaîne vide comme dernier élément du résultat si la chaîne d'entrée se termine par le modèle.Je convertis en String [] pour plus de cohérence avec
Pattern#split
, j'utilisenew String[0]
plutôt quenew String[result.size()]
, voir ici pourquoi.Voici mes tests:
la source
Je publierai également mes versions de travail (la première est vraiment similaire à Markus).
Et voici la deuxième solution et sa ronde 50% plus rapide que la première:
la source
Une autre solution candidate utilisant une expression régulière. Conserve l'ordre des jetons, correspond correctement à plusieurs jetons du même type d'affilée. L'inconvénient est que le regex est un peu méchant.
Exemple de sortie:
la source
Je ne connais pas de fonction existante dans l'API Java qui fait cela (ce qui ne veut pas dire qu'elle n'existe pas), mais voici ma propre implémentation (un ou plusieurs délimiteurs seront retournés comme un seul jeton; si vous voulez chaque délimiteur doit être retourné en tant que jeton séparé, il faudra un peu d'adaptation):
la source
Je suggère d'utiliser Pattern and Matcher, qui permettra presque certainement d'atteindre ce que vous voulez. Votre expression régulière devra être un peu plus compliquée que celle que vous utilisez dans String.split.
la source
Je ne pense pas que ce soit possible avec
String#split
, mais vous pouvez utiliser unStringTokenizer
, bien que cela ne vous permette pas de définir votre délimiteur comme une expression régulière, mais uniquement comme une classe de caractères à un chiffre:la source
Si vous pouvez vous le permettre, utilisez la méthode Java replace (CharSequence target, CharSequence replacement) et remplissez un autre délimiteur pour vous séparer. Exemple: je veux diviser la chaîne "boo: and: foo" et garder ':' à sa chaîne de droite.
Remarque importante: cela ne fonctionne que si vous n'avez plus de "nouveau délimiteur" dans votre chaîne! Ce n'est donc pas une solution générale. Mais si vous connaissez une CharSequence dont vous pouvez être sûr qu'elle n'apparaîtra jamais dans la chaîne, c'est une solution très simple.
la source
Réponse rapide: utilisez des limites non physiques comme \ b pour diviser. Je vais essayer et expérimenter pour voir si ça marche (utilisé ça en PHP et JS).
C'est possible, et genre de travail, mais ça risque de se diviser trop. En fait, cela dépend de la chaîne que vous souhaitez diviser et du résultat dont vous avez besoin. Donnez plus de détails, nous vous aiderons mieux.
Une autre façon est de faire votre propre fractionnement, de capturer le délimiteur (en supposant qu'il soit variable) et de l'ajouter ensuite au résultat.
Mon test rapide:
Résultat:
Un peu trop... :-)
la source
Tweaked Pattern.split () pour inclure le motif correspondant à la liste
Ajoutée
Source complète
la source
Voici une version groovy basée sur une partie du code ci-dessus, au cas où cela aiderait. C'est court, en tout cas. Comprend conditionnellement la tête et la queue (si elles ne sont pas vides). La dernière partie est une démonstration / test.
la source
Une solution extrêmement naïve et inefficace qui fonctionne quand même: utilisez le split deux fois sur la chaîne puis concaténez les deux tableaux
la source
la source
Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); }
L'une des subtilités de cette question implique la question "délimiteur principal": si vous allez avoir un tableau combiné de jetons et de délimiteurs, vous devez savoir s'il commence par un jeton ou un délimiteur. Vous pouvez bien sûr simplement supposer qu'un délimage de tête doit être jeté, mais cela semble une hypothèse injustifiée. Vous voudrez peut-être également savoir si vous avez un délimiteur de fin ou non. Cela définit deux drapeaux booléens en conséquence.
Écrit en Groovy mais une version Java devrait être assez évidente:
la source
Je ne connais pas trop bien Java, mais si vous ne trouvez pas une méthode Split qui fait ça, je vous suggère de faire la vôtre.
Ce n'est pas trop élégant, mais ça ira.
la source