Comment diviser une chaîne, mais aussi garder les délimiteurs?
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 correspond au délimiteur regex.
En d'autres termes, c'est ce que je reçois:
Text1
Text2
Text3
Text4
C'est ce que je envie
Text1
DelimiterA
Text2
DelimiterC
Text3
DelimiterB
Text4
Existe-t-il un moyen JDK de diviser la chaîne en utilisant un délimiteur regex mais aussi de conserver les délimiteurs?
23 réponses
Vous pouvez utiliser Lookahead et Lookbehind. Comme ceci:
System.out.println(Arrays.toString("a;b;c;d".split("(?<=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("(?=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("((?<=;)|(?=;))")));
Et vous obtiendrez:
[a;, b;, c;, d]
[a, ;b, ;c, ;d]
[a, ;, b, ;, c, ;, d]
Le dernier est ce que vous voulez.
((?<=;)|(?=;))
égal à pour sélectionner un caractère vide avant ;
ou après ;
.
J'espère que cela aide.
EDIT Fabian Steeg commentaires sur la lisibilité est valide. La lisibilité est toujours le problème pour RegEx. Une chose, je fais pour aider à faciliter cela est de créer une variable dont le nom représente ce que fait l'expression rationnelle et utilise le format de chaîne Java pour aider ça. Comme ceci:
static public final String WITH_DELIMITER = "((?<=%1$s)|(?=%1$s))";
...
public void someMethod() {
...
final String[] aEach = "a;b;c;d".split(String.format(WITH_DELIMITER, ";"));
...
}
...
Cela aide un peu. :- D
Vous voulez utiliser lookarounds, et diviser sur les correspondances de largeur nulle. Voici quelques exemples:
public class SplitNDump {
static void dump(String[] arr) {
for (String s : arr) {
System.out.format("[%s]", s);
}
System.out.println();
}
public static void main(String[] args) {
dump("1,234,567,890".split(","));
// "[1][234][567][890]"
dump("1,234,567,890".split("(?=,)"));
// "[1][,234][,567][,890]"
dump("1,234,567,890".split("(?<=,)"));
// "[1,][234,][567,][890]"
dump("1,234,567,890".split("(?<=,)|(?=,)"));
// "[1][,][234][,][567][,][890]"
dump(":a:bb::c:".split("(?=:)|(?<=:)"));
// "[][:][a][:][bb][:][:][c][:]"
dump(":a:bb::c:".split("(?=(?!^):)|(?<=:)"));
// "[:][a][:][bb][:][:][c][:]"
dump(":::a::::b b::c:".split("(?=(?!^):)(?<!:)|(?!:)(?<=:)"));
// "[:::][a][::::][b b][::][c][:]"
dump("a,bb:::c d..e".split("(?!^)\\b"));
// "[a][,][bb][:::][c][ ][d][..][e]"
dump("ArrayIndexOutOfBoundsException".split("(?<=[a-z])(?=[A-Z])"));
// "[Array][Index][Out][Of][Bounds][Exception]"
dump("1234567890".split("(?<=\\G.{4})"));
// "[1234][5678][90]"
// Split at the end of each run of letter
dump("Boooyaaaah! Yippieeee!!".split("(?<=(?=(.)\\1(?!\\1))..)"));
// "[Booo][yaaaa][h! Yipp][ieeee][!!]"
}
}
Et oui, c'est une affirmation Triple imbriquée dans le dernier modèle.
Questions connexes
- Java split mange mes personnages.
- pouvez-vous utiliser une expression rationnelle correspondant à une largeur nulle dans la division de chaînes?
- Comment convertir CamelCase en noms lisibles par L'homme en Java?
- références arrières dans lookbehind
Voir aussi
Une solution très naïve, qui n'implique pas regex serait d'effectuer un remplacement de chaîne sur votre délimiteur le long des lignes de (en supposant une virgule pour délimiteur):
string.replace(FullString, "," , "~,~")
Où vous pouvez remplacer tilda ( ~ ) par un délimiteur unique approprié.
Ensuite, si vous faites une Division sur votre nouveau délimiteur, je crois que vous obtiendrez le résultat souhaité.
import java.util.regex.*;
import java.util.LinkedList;
public class Splitter {
private static final Pattern DEFAULT_PATTERN = Pattern.compile("\\s+");
private Pattern pattern;
private boolean keep_delimiters;
public Splitter(Pattern pattern, boolean keep_delimiters) {
this.pattern = pattern;
this.keep_delimiters = keep_delimiters;
}
public Splitter(String pattern, boolean keep_delimiters) {
this(Pattern.compile(pattern==null?"":pattern), keep_delimiters);
}
public Splitter(Pattern pattern) { this(pattern, true); }
public Splitter(String pattern) { this(pattern, true); }
public Splitter(boolean keep_delimiters) { this(DEFAULT_PATTERN, keep_delimiters); }
public Splitter() { this(DEFAULT_PATTERN); }
public String[] split(String text) {
if (text == null) {
text = "";
}
int last_match = 0;
LinkedList<String> splitted = new LinkedList<String>();
Matcher m = this.pattern.matcher(text);
while (m.find()) {
splitted.add(text.substring(last_match,m.start()));
if (this.keep_delimiters) {
splitted.add(m.group());
}
last_match = m.end();
}
splitted.add(text.substring(last_match));
return splitted.toArray(new String[splitted.size()]);
}
public static void main(String[] argv) {
if (argv.length != 2) {
System.err.println("Syntax: java Splitter <pattern> <text>");
return;
}
Pattern pattern = null;
try {
pattern = Pattern.compile(argv[0]);
}
catch (PatternSyntaxException e) {
System.err.println(e);
return;
}
Splitter splitter = new Splitter(pattern);
String text = argv[1];
int counter = 1;
for (String part : splitter.split(text)) {
System.out.printf("Part %d: \"%s\"\n", counter++, part);
}
}
}
/*
Example:
> java Splitter "\W+" "Hello World!"
Part 1: "Hello"
Part 2: " "
Part 3: "World"
Part 4: "!"
Part 5: ""
*/
Je n'aime pas vraiment l'inverse, 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 gaspiller deux bons emplacements de tableau.
Edit: cas limites fixes. La source commentée avec des cas de test peut être trouvée ici: http://snippets.dzone.com/posts/show/6453
Je suis arrivé en retard, mais pour revenir à la question initiale, pourquoi ne pas simplement utiliser des lookarounds?
Pattern p = Pattern.compile("(?<=\\w)(?=\\W)|(?<=\\W)(?=\\w)");
System.out.println(Arrays.toString(p.split("'ab','cd','eg'")));
System.out.println(Arrays.toString(p.split("boo:and:foo")));
Sortie:
[', ab, ',', cd, ',', eg, ']
[boo, :, and, :, foo]
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 garder une trace des virgules qui font partie du résultat et qui ont été ajoutées par Arrays.toString()
. La coloration syntaxique de SO n'aide pas non plus. Dans l'espoir de faire en sorte que la mise en évidence fonctionne avec {[10] } moi au lieu de contre moi, voici à quoi ressembleraient ces tableaux je les déclarais dans le code source:
{ "'", "ab", "','", "cd", "','", "eg", "'" }
{ "boo", ":", "and", ":", "foo" }
J'espère que c'est plus facile à lire. Merci pour le heads-up, @ finnw.
J'ai regardé les réponses ci-dessus et honnêtement, aucune d'entre elles ne me semble satisfaisante. Ce que vous voulez faire est essentiellement d'imiter la fonctionnalité Perl split. Pourquoi Java ne le permet pas et a une méthode join () quelque part est au-delà de moi mais je m'égare. Vous n'avez même pas besoin d'un cours pour cela vraiment. C'est juste une fonction. Exécutez cet exemple de programme:
Certaines des réponses précédentes ont une vérification nulle excessive, que j'ai récemment écrit une réponse à une question ici:
Https://stackoverflow.com/users/18393/cletus
Quoi Qu'il en soit, le code:
public class Split {
public static List<String> split(String s, String pattern) {
assert s != null;
assert pattern != null;
return split(s, Pattern.compile(pattern));
}
public static List<String> split(String s, Pattern pattern) {
assert s != null;
assert pattern != null;
Matcher m = pattern.matcher(s);
List<String> ret = new ArrayList<String>();
int start = 0;
while (m.find()) {
ret.add(s.substring(start, m.start()));
ret.add(m.group());
start = m.end();
}
ret.add(start >= s.length() ? "" : s.substring(start));
return ret;
}
private static void testSplit(String s, String pattern) {
System.out.printf("Splitting '%s' with pattern '%s'%n", s, pattern);
List<String> tokens = split(s, pattern);
System.out.printf("Found %d matches%n", tokens.size());
int i = 0;
for (String token : tokens) {
System.out.printf(" %d/%d: '%s'%n", ++i, tokens.size(), token);
}
System.out.println();
}
public static void main(String args[]) {
testSplit("abcdefghij", "z"); // "abcdefghij"
testSplit("abcdefghij", "f"); // "abcde", "f", "ghi"
testSplit("abcdefghij", "j"); // "abcdefghi", "j", ""
testSplit("abcdefghij", "a"); // "", "a", "bcdefghij"
testSplit("abcdefghij", "[bdfh]"); // "a", "b", "c", "d", "e", "f", "g", "h", "ij"
}
}
Je sais que c'est une très très vieille question, et la réponse a également été accepté. Mais je voudrais encore soumettre une réponse très simple à la question initiale. Considérez ce code:
String str = "Hello-World:How\nAre You&doing";
inputs = str.split("(?!^)\\b");
for (int i=0; i<inputs.length; i++) {
System.out.println("a[" + i + "] = \"" + inputs[i] + '"');
}
SORTIE:
a[0] = "Hello"
a[1] = "-"
a[2] = "World"
a[3] = ":"
a[4] = "How"
a[5] = "
"
a[6] = "Are"
a[7] = " "
a[8] = "You"
a[9] = "&"
a[10] = "doing"
J'utilise juste la limite de mot \b
pour délimiter les mots sauf Quand c'est le début du texte.
J'aime L'idée de StringTokenizer car elle est énumérable.
Mais il est également obsolète, et remplacer par une chaîne.split qui renvoie une chaîne ennuyeuse[] (et n'inclut pas les délimiteurs).
J'ai donc implémenté un StringTokenizerEx qui est itérable, et qui prend une véritable 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 vides chaîne à l'intérieur:
[o], '', [o], '', [o]
Mais l'expression rationnelle o + retournera le résultat attendu lors de la division de "aooob"
[], 'a', [ooo], 'b', []
Pour utiliser ce StringTokenizerEx:
final StringTokenizerEx aStringTokenizerEx = new StringTokenizerEx("boo:and:foo", "o+");
final String firstDelimiter = aStringTokenizerEx.getDelimiter();
for(String aString: aStringTokenizerEx )
{
// uses the split String detected and memorized in 'aString'
final nextDelimiter = aStringTokenizerEx.getDelimiter();
}
Le code de cette classe est disponible à DZone Extraits.
, Comme d'habitude pour un code-défi réponse (un classe avec des cas de test inclus), copier-coller (dans un 'src/test" directory) et de exécuter. Sa méthode main() illustre le des usages différents.
Note: (édition fin 2009)
Oui, c'est une douleur. FWIW, cela a été fait pour une très bonne raison: la compatibilité avec Perl.
Le gars qui l'a fait est Mike "madbot" McCloskey, qui travaille maintenant avec nous chez Google. Mike s'est assuré que Java est régulier les expressions ont passé pratiquement tous les tests D'expression régulière Perl 30K (et ont couru plus vite).
Google communes-bibliothèque de Goyave contient également un Séparateur qui est:
- plus simple à utiliser
- maintenu par Google (et non par vous)
Il peut donc être utile d'être vérifié. De leur documentation approximative initiale (pdf):
JDK a ceci:
String[] pieces = "foo.bar".split("\\.");
C'est bien d'utiliser ceci si vous voulez exactement ce qu'il fait: - expression régulière - le résultat dans un tableau - sa façon de manipuler les pièces vides
Mini-jeu de casse-tête: ",a,,b,".split(",") renvoie...
(a) "", "a", "", "b", ""
(b) null, "a", null, "b", null
(c) "a", null, "b"
(d) "a", "b"
(e) None of the above
Réponse: e) rien de ce qui précède.
",a,,b,".split(",")
returns
"", "a", "", "b"
Seuls les vides de fin sont ignorés! (Qui connaît la solution de contournement pour empêcher le saut? C'est un plaisir...)
Dans tous les cas, notre séparateur est simplement plus flexible: le comportement par défaut est simpliste:
Splitter.on(',').split(" foo, ,bar, quux,")
--> [" foo", " ", "bar", " quux", ""]
Si vous voulez des fonctionnalités supplémentaires, demandez-les!
Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split(" foo, ,bar, quux,")
--> ["foo", "bar", "quux"]
L'Ordre des méthodes de configuration n'a pas d'importance-pendant le fractionnement, le rognage se produit avant de vérifier les vides.
Voici une implémentation simple et propre qui est cohérente avec Pattern#split
et fonctionne avec des modèles de longueur variable, qui ne peuvent pas supporter, et il est plus facile à utiliser. Il est similaire à la solution fournie par @cletus.
public static String[] split(CharSequence input, String pattern) {
return split(input, Pattern.compile(pattern));
}
public static String[] split(CharSequence input, Pattern pattern) {
Matcher matcher = pattern.matcher(input);
int start = 0;
List<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(input.subSequence(start, matcher.start()).toString());
result.add(matcher.group());
start = matcher.end();
}
if (start != input.length()) result.add(input.subSequence(start, input.length()).toString());
return result.toArray(new String[0]);
}
Je ne fais pas de vérifications nulles ici, Pattern#split
ne le fait pas, pourquoi je n'aime pas le if
à la fin mais il est nécessaire pour la cohérence avec le Pattern#split
. Sinon, je voudrais ajouter inconditionnellement, résultant en une chaîne vide comme dernier élément de le résultat si la chaîne d'entrée se termine par le motif.
Je convertir String[] pour des raisons de cohérence avec Pattern#split
, j'utilise new String[0]
plutôt que new String[result.size()]
, voir ici pourquoi.
Voici mes tests:
@Test
public void splitsVariableLengthPattern() {
String[] result = Split.split("/foo/$bar/bas", "\\$\\w+");
Assert.assertArrayEquals(new String[] { "/foo/", "$bar", "/bas" }, result);
}
@Test
public void splitsEndingWithPattern() {
String[] result = Split.split("/foo/$bar", "\\$\\w+");
Assert.assertArrayEquals(new String[] { "/foo/", "$bar" }, result);
}
@Test
public void splitsStartingWithPattern() {
String[] result = Split.split("$foo/bar", "\\$\\w+");
Assert.assertArrayEquals(new String[] { "", "$foo", "/bar" }, result);
}
@Test
public void splitsNoMatchesPattern() {
String[] result = Split.split("/foo/bar", "\\$\\w+");
Assert.assertArrayEquals(new String[] { "/foo/bar" }, result);
}
Passez le 3ème aurgument comme "vrai". Il retournera également des délimiteurs.
StringTokenizer(String str, String delimiters, true);
Je posterai aussi mes versions de travail (la première est vraiment similaire à Markus).
public static String[] splitIncludeDelimeter(String regex, String text){
List<String> list = new LinkedList<>();
Matcher matcher = Pattern.compile(regex).matcher(text);
int now, old = 0;
while(matcher.find()){
now = matcher.end();
list.add(text.substring(old, now));
old = now;
}
if(list.size() == 0)
return new String[]{text};
//adding rest of a text as last element
String finalElement = text.substring(old);
list.add(finalElement);
return list.toArray(new String[list.size()]);
}
Et voici la deuxième solution et son tour 50% plus rapide que le premier:
public static String[] splitIncludeDelimeter2(String regex, String text){
List<String> list = new LinkedList<>();
Matcher matcher = Pattern.compile(regex).matcher(text);
StringBuffer stringBuffer = new StringBuffer();
while(matcher.find()){
matcher.appendReplacement(stringBuffer, matcher.group());
list.add(stringBuffer.toString());
stringBuffer.setLength(0); //clear buffer
}
matcher.appendTail(stringBuffer); ///dodajemy reszte ciagu
list.add(stringBuffer.toString());
return list.toArray(new String[list.size()]);
}
Je ne connais pas une 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 que chaque délimiteur soit retourné comme un jeton séparé, il faudra un peu d'adaptation):
static String[] splitWithDelimiters(String s) {
if (s == null || s.length() == 0) {
return new String[0];
}
LinkedList<String> result = new LinkedList<String>();
StringBuilder sb = null;
boolean wasLetterOrDigit = !Character.isLetterOrDigit(s.charAt(0));
for (char c : s.toCharArray()) {
if (Character.isLetterOrDigit(c) ^ wasLetterOrDigit) {
if (sb != null) {
result.add(sb.toString());
}
sb = new StringBuilder();
wasLetterOrDigit = !wasLetterOrDigit;
}
sb.append(c);
}
result.add(sb.toString());
return result.toArray(new String[0]);
}
Je suggère d'utiliser Pattern et Matcher, ce qui permettra presque certainement d'atteindre ce que vous voulez. Votre expression régulière devra être un peu plus compliquée que ce que vous utilisez dans String.Split.
Je ne pense pas que ce soit possible avec String#split
, mais vous pouvez utiliser un StringTokenizer
, bien que cela ne vous permette pas de définir votre délimiteur comme une expression rationnelle, mais seulement comme une classe de caractères à un chiffre:
new StringTokenizer("Hello, world. Hi!", ",.!", true); // true for returnDelims
Si vous pouvez vous le permettre, utilisez la méthode replace(CharSequence target, CharSequence replacement) de Java et remplissez un autre délimiteur avec lequel diviser. Exemple: Je veux diviser la chaîne "boo: and: foo" et garder ': 'à sa chaîne de droite.
String str = "boo:and:foo";
str = str.replace(":","newdelimiter:");
String[] tokens = str.split("newdelimiter");
Remarque importante: cela ne fonctionne que si vous n'avez plus de "newdelimiter" dans votre chaîne! Ce n'est donc pas une solution générale. Mais si vous connaissez un CharSequence dont vous pouvez être sûr qu'il n'apparaîtra jamais dans la chaîne, c'est très simple solution.
Une autre solution candidate utilisant une expression rationnelle. Conserve l'ordre des jetons, correspond correctement à plusieurs jetons du même type dans une rangée. L'inconvénient est que le regex est un peu méchant.
package javaapplication2;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavaApplication2 {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String num = "58.5+variable-+98*78/96+a/78.7-3443*12-3";
// Terrifying regex:
// (a)|(b)|(c) match a or b or c
// where
// (a) is one or more digits optionally followed by a decimal point
// followed by one or more digits: (\d+(\.\d+)?)
// (b) is one of the set + * / - occurring once: ([+*/-])
// (c) is a sequence of one or more lowercase latin letter: ([a-z]+)
Pattern tokenPattern = Pattern.compile("(\\d+(\\.\\d+)?)|([+*/-])|([a-z]+)");
Matcher tokenMatcher = tokenPattern.matcher(num);
List<String> tokens = new ArrayList<>();
while (!tokenMatcher.hitEnd()) {
if (tokenMatcher.find()) {
tokens.add(tokenMatcher.group());
} else {
// report error
break;
}
}
System.out.println(tokens);
}
}
Exemple de sortie:
[58.5, +, variable, -, +, 98, *, 78, /, 96, +, a, /, 78.7, -, 3443, *, 12, -, 3]
Réponse rapide: utilisez des limites non physiques comme \b pour diviser. Je vais essayer d'expérimenter pour voir si cela fonctionne (utilisé en PHP et JS).
Il est possible, et le genre de travail, mais pourrait diviser trop. En fait, cela dépend de la chaîne que vous voulez diviser et du résultat dont vous avez besoin. Donner plus de détails, nous allons vous aider à mieux.
Une autre façon est de faire votre propre division, en capturant le délimiteur (en supposant qu'il soit variable) et en l'ajoutant ensuite au résultat.
Mon rapide essai:
String str = "'ab','cd','eg'";
String[] stra = str.split("\\b");
for (String s : stra) System.out.print(s + "|");
System.out.println();
Résultat:
'|ab|','|cd|','|eg|'|
Un peu trop... :-)
Modèle Modifié .split () pour inclure le motif correspondant à la liste
Ajouté
// add match to the list
matchList.add(input.subSequence(start, end).toString());
Source Complet
public static String[] inclusiveSplit(String input, String re, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<String>();
Pattern pattern = Pattern.compile(re);
Matcher m = pattern.matcher(input);
// Add segments before each match found
while (m.find()) {
int end = m.end();
if (!matchLimited || matchList.size() < limit - 1) {
int start = m.start();
String match = input.subSequence(index, start).toString();
matchList.add(match);
// add match to the list
matchList.add(input.subSequence(start, end).toString());
index = end;
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index, input.length())
.toString();
matchList.add(match);
index = end;
}
}
// If no match was found, return this
if (index == 0)
return new String[] { input.toString() };
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize - 1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Voici une version groovy basée sur une partie du code ci-dessus, au cas où cela aiderait. C'est court, de toute façon. Comprend conditionnellement la tête et la queue (si elles ne sont pas vides). La dernière partie est un cas de démonstration / test.
List splitWithTokens(str, pat) {
def tokens=[]
def lastMatch=0
def m = str=~pat
while (m.find()) {
if (m.start() > 0) tokens << str[lastMatch..<m.start()]
tokens << m.group()
lastMatch=m.end()
}
if (lastMatch < str.length()) tokens << str[lastMatch..<str.length()]
tokens
}
[['<html><head><title>this is the title</title></head>',/<[^>]+>/],
['before<html><head><title>this is the title</title></head>after',/<[^>]+>/]
].each {
println splitWithTokens(*it)
}
Une solution extrêmement naïve et inefficace qui fonctionne néanmoins.Utilisez split deux fois sur la chaîne, puis concaténez les deux tableaux
String temp[]=str.split("\\W");
String temp2[]=str.split("\\w||\\s");
int i=0;
for(String string:temp)
System.out.println(string);
String temp3[]=new String[temp.length-1];
for(String string:temp2)
{
System.out.println(string);
if((string.equals("")!=true)&&(string.equals("\\s")!=true))
{
temp3[i]=string;
i++;
}
// System.out.println(temp.length);
// System.out.println(temp2.length);
}
System.out.println(temp3.length);
String[] temp4=new String[temp.length+temp3.length];
int j=0;
for(i=0;i<temp.length;i++)
{
temp4[j]=temp[i];
j=j+2;
}
j=1;
for(i=0;i<temp3.length;i++)
{
temp4[j]=temp3[i];
j+=2;
}
for(String s:temp4)
System.out.println(s);
String expression = "((A+B)*C-D)*E";
expression = expression.replaceAll("\\+", "~+~");
expression = expression.replaceAll("\\*", "~*~");
expression = expression.replaceAll("-", "~-~");
expression = expression.replaceAll("/+", "~/~");
expression = expression.replaceAll("\\(", "~(~"); //also you can use [(] instead of \\(
expression = expression.replaceAll("\\)", "~)~"); //also you can use [)] instead of \\)
expression = expression.replaceAll("~~", "~");
if(expression.startsWith("~")) {
expression = expression.substring(1);
}
String[] expressionArray = expression.split("~");
System.out.println(Arrays.toString(expressionArray));
Si vous êtes inquiet au sujet des complications look-ahead / look-behind stuff pourrait introduire, et je veux juste une méthode utilitaire solide comme le roc qui peut faire face à tout modèle de jeton et tout séparateurs vous jeter à elle. (Ce qui est probablement le cas!)
NB surpris de constater que les gens D'Apache Commons ne semblent pas avoir fourni cela, par exemple dans StringUtils
.
Aussi, je suggère que cela devrait être un drapeau dans Pattern
: I..e INCLUDE_SEPARATORS
.
Mais c'est assez simple si vous utilisez les classes Pattern
et Matcher
right:
// NB could be a different spec for identifying tokens, of course!
Pattern sepAndTokenPattern = Pattern.compile("(.*?)(\\w+)");
Matcher matcher = sepAndTokenPattern.matcher( stringForTokenising );
List<String> tokenAndSeparatorList = new ArrayList<String>();
// for most processing purposes you are going to want to know whether your
// combined list of tokens and separators begins with a token or separator
boolean startsWithToken = true;
int matchEnd = -1;
while (matcher.find()) {
String preSep = matcher.group(1);
if (!preSep.isEmpty()) {
if( tokenAndSeparatorList.isEmpty() ){
startsWithToken = false;
}
// in implementation you wouldn't want these | characters, of course
tokenAndSeparatorList.add("|" + preSep + "|"); // add sep
}
tokenAndSeparatorList.add("|" + matcher.group(2) + "|"); // add token
matchEnd = matcher.end();
}
// get trailing separator, if there is one:
if( matchEnd != -1 ){
String trailingSep = stringForTokenising.substring( matchEnd );
if( ! trailingSep.isEmpty() ){
tokenAndSeparatorList.add( "|" + trailingSep + "|" );
}
}
System.out.println(String.format("# starts with token? %b - matchList %s", startsWithToken, tokenAndSeparatorList));
Je ne connais pas trop bien Java, mais si vous ne trouvez pas une méthode Split qui le fait, je vous suggère de créer la vôtre.
string[] mySplit(string s,string delimiter)
{
string[] result = s.Split(delimiter);
for(int i=0;i<result.Length-1;i++)
{
result[i] += delimiter; //this one would add the delimiter to each items end except the last item,
//you can modify it however you want
}
}
string[] res = mySplit(myString,myDelimiter);
Ce N'est pas trop élégant, mais ça fera l'affaire.