Как разбить строку, но также сохранить разделители?
У меня есть многострочная строка, которая ограничена набором различных разделителей:
(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)
Я могу разбить эту строку на части, используя String.split
, но кажется, что я не могу получить фактическую строку, которая соответствует регулярному выражению разделителя.
Другими словами, это то, что я получаю:
Text1
Text2
Text3
Text4
Это то что я хочу
Text1
DelimiterA
Text2
DelimiterC
Text3
DelimiterB
Text4
Есть ли способ JDK разделить строку с помощью регулярного выражения разделителя, но также сохранить разделители?
25 ответов
Вы можете использовать Lookahead и Lookbehind. Как это:
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("((?<=;)|(?=;))")));
И вы получите:
[a;, b;, c;, d]
[a, ;b, ;c, ;d]
[a, ;, b, ;, c, ;, d]
Последнее, что вы хотите.
((?<=;)|(?=;))
равняется выбору пустого символа перед ;
или после ;
,
Надеюсь это поможет.
РЕДАКТИРОВАТЬ Фабиан Стиг комментирует Читаемость является действительным. Читаемость всегда является проблемой для RegEx. Одна вещь, которую я делаю, чтобы облегчить это, - создать переменную, имя которой представляет то, что делает регулярное выражение, и использовать формат Java String, чтобы помочь этому. Как это:
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, ";"));
...
}
...
Это немного помогает.:-D
Вы хотите использовать обходные пути и разбивать на совпадения нулевой ширины. Вот некоторые примеры:
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][!!]"
}
}
И да, это трижды вложенное утверждение в последнем паттерне.
Смежные вопросы
- Сплит Java пожирает моих персонажей.
- Можете ли вы использовать регулярное выражение соответствия нулевой ширины в String split?
- Как мне преобразовать CamelCase в понятные человеку имена в Java?
- Обратные ссылки в вид сзади
Смотрите также
Очень наивное решение, которое не включает в себя регулярное выражение, заключалось бы в выполнении замены строки в вашем разделителе по типу (при условии использования запятой для разделителя):
string.replace(FullString, "," , "~,~")
Где вы можете заменить тильду (~) соответствующим уникальным разделителем.
Тогда, если вы сделаете разделение на свой новый разделитель, то я верю, что вы получите желаемый результат.
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: ""
*/
Мне не очень нравится другой путь, когда вы получаете пустой элемент спереди и сзади. Разделитель обычно находится не в начале или в конце строки, поэтому чаще всего вы теряете два хороших слота массива.
Редактировать: Фиксированный лимит случаев. Комментируемый источник с тестовыми примерами можно найти здесь: http://snippets.dzone.com/posts/show/6453
Передача 3-го aurgument как "правда". Он также вернет разделители.
StringTokenizer(String str, String delimiters, true);
Я знаю, что это очень-очень старый вопрос, и ответ также был принят. Но все же я хотел бы представить очень простой ответ на оригинальный вопрос. Рассмотрим этот код:
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] + '"');
}
ВЫХОД:
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"
Я просто использую границу слова \b
Разграничить слова, кроме случаев, когда это начало текста.
Я пришел сюда поздно, но возвращаясь к первоначальному вопросу, почему бы просто не использовать 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")));
выход:
[', ab, ',', cd, ',', eg, ']
[boo, :, and, :, foo]
РЕДАКТИРОВАТЬ: То, что вы видите выше, это то, что появляется в командной строке, когда я запускаю этот код, но теперь я вижу, что это немного сбивает с толку. Трудно отследить, какие запятые являются частью результата, а какие были добавлены Arrays.toString()
, Подсветка синтаксиса SO тоже не помогает. В надежде заставить подсветку работать со мной, а не против меня, вот как будут выглядеть эти массивы, я объявил их в исходном коде:
{ "'", "ab", "','", "cd", "','", "eg", "'" }
{ "boo", ":", "and", ":", "foo" }
Я надеюсь, что это легче читать. Спасибо за хедз-ап, @finnw.
Я взглянул на приведенные выше ответы и, честно говоря, ни один из них я не нашел удовлетворительным. То, что вы хотите сделать, по сути, имитировать функциональность разделения Perl. Почему в Java это не разрешено и где-то есть метод join(), я не знаю, но я отвлекся. Тебе даже не нужен класс для этого. Это просто функция. Запустите этот пример программы:
У некоторых из более ранних ответов есть чрезмерная проверка нуля, на которую я недавно написал ответ на вопрос здесь:
Во всяком случае, код:
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"
}
}
Вот простая чистая реализация, которая соответствует Pattern#split
и работает с шаблонами переменной длины, которые не поддерживаются, и их проще использовать. Это похоже на решение, предоставленное @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]);
}
Я не делаю здесь нулевых проверок, Pattern#split
нет, почему я. Мне не нравится if
в конце, но это требуется для согласованности с Pattern#split
, В противном случае я бы безоговорочно добавил, что привело бы к пустой строке в качестве последнего элемента результата, если входная строка заканчивается шаблоном.
Я преобразую в строку [] для согласованности с Pattern#split
, Я использую new String[0]
скорее, чем new String[result.size()]
смотрите здесь почему.
Вот мои тесты:
@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);
}
Мне нравится идея StringTokenizer, потому что это Enumerable.
Но он также устарел и заменяется на String.split, который возвращает скучную строку [] (и не включает разделители).
Поэтому я реализовал StringTokenizerEx, который является Iterable и для которого требуется истинное регулярное выражение для разбиения строки.
Истинное регулярное выражение означает, что это не "последовательность символов", повторяемая для формирования разделителя:
"o" будет соответствовать только "o" и разделит "ooo" на три разделителя с двумя пустыми строками внутри:
[o], '', [o], '', [o]
Но регулярное выражение o + вернет ожидаемый результат при разбиении "aooob"
[], 'a', [ooo], 'b', []
Чтобы использовать этот 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();
}
Код этого класса доступен на DZone Snippets.
Как обычно для ответа на вызов кода (один автономный класс с включенными тестовыми примерами), скопируйте и вставьте его (в каталог 'src/test') и запустите его. Его метод main() иллюстрирует различные способы использования.
Примечание: (конец 2009 года редактировать)
Заключительные мысли: Java Puzzler: Splitting Hairs проделывает хорошую работу, объясняя причудливое поведение в String.split()
,
Джош Блох даже прокомментировал в ответ на эту статью:
Да, это боль. FWIW, это было сделано по очень веской причине: совместимость с Perl.
Парнем, который сделал это, является Майк "сумасшедший" Макклоски, который сейчас работает с нами в Google. Майк позаботился о том, чтобы регулярные выражения Java проходили практически все тесты регулярных выражений Perl 30K (и работали быстрее).
Общая библиотека Google Guava содержит также разделитель, который:
- проще в использовании
- поддерживается Google (а не вами)
Так что, возможно, стоит проверить. Из их исходной грубой документации (pdf):
У JDK есть это:
String[] pieces = "foo.bar".split("\\.");
Хорошо использовать это, если вы хотите именно то, что он делает: - регулярное выражение - результат в виде массива - его способ обработки пустых частей
Мини-головоломка: ",a,,b,". Split(",") возвращает...
(a) "", "a", "", "b", ""
(b) null, "a", null, "b", null
(c) "a", null, "b"
(d) "a", "b"
(e) None of the above
Ответ: (д) Ничего из вышеперечисленного.
",a,,b,".split(",")
returns
"", "a", "", "b"
Пропускаются только конечные тары! (Кто знает обходной путь для предотвращения пропуска? Это весело...)
В любом случае наш Splitter просто более гибок: поведение по умолчанию упрощено:
Splitter.on(',').split(" foo, ,bar, quux,")
--> [" foo", " ", "bar", " quux", ""]
Если вы хотите дополнительные функции, попросите их!
Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split(" foo, ,bar, quux,")
--> ["foo", "bar", "quux"]
Порядок методов конфигурации не имеет значения - во время разделения, обрезка происходит перед проверкой на пустые места.
Я также опубликую свои рабочие версии (первая действительно похожа на 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()]);
}
И вот второе решение и его на 50% быстрее, чем первое:
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()]);
}
Если вы можете себе позволить, используйте Java метод replace(цель CharSequence, замена CharSequence) и заполните другой разделитель для разделения. Пример: я хочу разделить строку "boo:and:foo" и оставить строку ":" в правой строке.
String str = "boo:and:foo";
str = str.replace(":","newdelimiter:");
String[] tokens = str.split("newdelimiter");
Важное примечание: это работает только в том случае, если в вашей строке больше нет "newdelimiter"! Таким образом, это не общее решение. Но если вы знаете последовательность CharSequence, в которой вы можете быть уверены, что она никогда не появится в строке, это очень простое решение.
Другой вариант решения с использованием регулярных выражений. Сохраняет порядок токенов, правильно сопоставляет несколько токенов одного типа подряд. Недостатком является то, что регулярное выражение является противным.
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);
}
}
Образец вывода:
[58.5, +, variable, -, +, 98, *, 78, /, 96, +, a, /, 78.7, -, 3443, *, 12, -, 3]
Я не думаю, что это возможно с String#split
, но вы можете использовать StringTokenizer
хотя это не позволит вам определить разделитель как регулярное выражение, а только как класс однозначных символов:
new StringTokenizer("Hello, world. Hi!", ",.!", true); // true for returnDelims
Я предлагаю использовать Pattern и Matcher, которые почти наверняка достигнут того, что вы хотите. Ваше регулярное выражение должно быть несколько сложнее, чем то, что вы используете в String.split.
Я не знаю о существующей функции в Java API, которая делает это (что не означает, что она не существует), но вот моя собственная реализация (один или несколько разделителей будут возвращены как один токен; если вы хотите каждый разделитель должен быть возвращен в виде отдельного токена, потребуется немного адаптации):
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]);
}
Если вы хотите сохранить характер, используйте метод разделения с лазейкой в
.split()
метод.
См. Этот пример:
public class SplitExample {
public static void main(String[] args) {
String str = "Javathomettt";
System.out.println("method 1");
System.out.println("Returning words:");
String[] arr = str.split("t", 40);
for (String w : arr) {
System.out.println(w+"t");
}
System.out.println("Split array length: "+arr.length);
System.out.println("method 2");
System.out.println(str.replaceAll("t", "\n"+"t"));
}
Вот отличная версия, основанная на коде выше, на случай, если это поможет. Во всяком случае, это коротко. Условно включает в себя голову и хвост (если они не пусты). Последняя часть представляет собой демонстрационный пример.
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)
}
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));
Если вы беспокоитесь о сложностях, которые могут возникнуть при просмотре вперед / назад, и вам нужен просто надежный служебный метод, который может справиться с любым шаблоном токена и любыми разделителями, которые вы к нему добавляете. (Что, вероятно, имеет место!)
NB удивлен, обнаружив, что люди из Apache Commons, кажется, не предоставили это, например, в StringUtils
,
Также я предлагаю, чтобы это был флаг в Pattern
: я..э INCLUDE_SEPARATORS
,
Но это довольно просто, если вы используете Pattern
а также Matcher
классы справа:
// 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));
Одна из тонкостей в этом вопросе связана с вопросом "ведущего разделителя": если у вас будет комбинированный массив токенов и разделителей, вы должны знать, начинается ли он с токена или разделителя. Конечно, вы можете просто предположить, что ведущий разделитель должен быть отброшен, но это кажется неоправданным предположением. Вы также можете узнать, есть ли у вас конечный разделитель или нет. Это устанавливает два логических флага соответственно.
Написано на Groovy, но версия Java должна быть довольно очевидной:
String tokenRegex = /[\p{L}\p{N}]+/ // a String in Groovy, Unicode alphanumeric
def finder = phraseForTokenising =~ tokenRegex
// NB in Groovy the variable 'finder' is then of class java.util.regex.Matcher
def finderIt = finder.iterator() // extra method added to Matcher by Groovy magic
int start = 0
boolean leadingDelim, trailingDelim
def combinedTokensAndDelims = [] // create an array in Groovy
while( finderIt.hasNext() )
{
def token = finderIt.next()
int finderStart = finder.start()
String delim = phraseForTokenising[ start .. finderStart - 1 ]
// Groovy: above gets slice of String/array
if( start == 0 ) leadingDelim = finderStart != 0
if( start > 0 || leadingDelim ) combinedTokensAndDelims << delim
combinedTokensAndDelims << token // add element to end of array
start = finder.end()
}
// start == 0 indicates no tokens found
if( start > 0 ) {
// finish by seeing whether there is a trailing delim
trailingDelim = start < phraseForTokenising.length()
if( trailingDelim ) combinedTokensAndDelims << phraseForTokenising[ start .. -1 ]
println( "leading delim? $leadingDelim, trailing delim? $trailingDelim, combined array:\n $combinedTokensAndDelims" )
}
Быстрый ответ: используйте нефизические границы, такие как \b, чтобы разделить. Я попытаюсь поэкспериментировать, чтобы увидеть, работает ли он (использовал это в PHP и JS).
Возможна и такая работа, но может расколоть слишком много. На самом деле, это зависит от строки, которую вы хотите разделить, и результата, который вам нужен. Дайте больше деталей, мы поможем вам лучше.
Другой способ - сделать ваше собственное разделение, захватив разделитель (предположим, что он является переменным) и добавив его позже к результату.
Мой быстрый тест:
String str = "'ab','cd','eg'";
String[] stra = str.split("\\b");
for (String s : stra) System.out.print(s + "|");
System.out.println();
Результат:
'|ab|','|cd|','|eg|'|
Слишком много...:-)
Чрезвычайно наивное и неэффективное решение, которое, тем не менее, работает. Используйте дважды разбитие на строку и затем объедините два массива
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);
Tweaked Pattern.split (), чтобы включить сопоставленный шаблон в список
добавленной
// add match to the list
matchList.add(input.subSequence(start, end).toString());
Полный источник
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);
}
Я не очень хорошо знаю Java, но если вы не можете найти метод Split, который делает это, я предлагаю вам сделать свой собственный.
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);
Это не слишком элегантно, но подойдет.