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?

180
demandé sur Daniel Rikowski 2010-02-05 13:00:57

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

284
répondu NawaMan 2013-11-24 10:17:07

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

Voir aussi

68
répondu polygenelubricants 2017-05-23 11:47:29

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é.

25
répondu chillysapien 2010-02-05 10:36:42
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

19
répondu Markus Jarderot 2008-11-10 08:21:29

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.

9
répondu Alan Moore 2009-11-10 17:38:03

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"
    }
}
8
répondu cletus 2017-05-23 12:34:45

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.

8
répondu anubhava 2011-04-13 02:38:21

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)

L'article Final Thoughts: Java Puzzler: fractionnement des cheveux {[24] } fait un bon travail expliquant le comportement bizarre dans String.split().
Josh Bloch a même commenté en réponse à cet article:

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.

4
répondu VonC 2017-05-23 10:31:36

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);
}
4
répondu Julian 2017-05-23 12:34:45

Passez le 3ème aurgument comme "vrai". Il retournera également des délimiteurs.

StringTokenizer(String str, String delimiters, true);
3
répondu Haseeb Jadoon 2015-03-10 14:55:19

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()]);
}
2
répondu Tomasz Mularczyk 2016-01-02 15:09:44

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]);
}
1
répondu bdumitriu 2008-11-09 09:59:09

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.

1
répondu Steve McLeod 2010-02-05 10:10:39

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
1
répondu Fabian Steeg 2010-02-05 11:07:43

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.

1
répondu Stephan 2010-03-24 13:59:39

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]
1
répondu Jarvis Cochrane 2017-11-30 13:09:43

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... :-)

0
répondu PhiLho 2008-11-09 10:08:31

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);
}
0
répondu Prashant Bhate 2011-07-05 15:21:55

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)
}
0
répondu miles zarathustra 2013-07-16 17:31:21

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);
0
répondu Varun Gangal 2013-12-31 09:26:25
    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));
0
répondu Kanagavelu Sugumar 2016-04-08 09:02:06

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));
0
répondu mike rodent 2018-04-17 11:29:58

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.

-2
répondu Alon L 2008-11-09 09:57:29