Как разбить строку "Thequickbrownfoxjumps"
на подстроки равного размера в Java. Например. "Thequickbrownfoxjumps"
4 одинаковых размера должны дать результат.
["Theq","uick","brow","nfox","jump","s"]
Аналогичный вопрос:
Как разбить строку "Thequickbrownfoxjumps"
на подстроки равного размера в Java. Например. "Thequickbrownfoxjumps"
4 одинаковых размера должны дать результат.
["Theq","uick","brow","nfox","jump","s"]
Аналогичный вопрос:
Ответы:
Вот однострочная версия регулярного выражения:
System.out.println(Arrays.toString(
"Thequickbrownfoxjumps".split("(?<=\\G.{4})")
));
\G
- это утверждение нулевой ширины, соответствующее позиции, в которой закончилось предыдущее совпадение. Если не было никакого предыдущего матча, он совпадает с началом ввода, так же , как \A
. Обращение назад соответствует позиции, которая находится на четыре символа от конца последнего совпадения.
Обе функции lookbehind и \G
расширенные функции регулярных выражений поддерживаются не всеми разновидностями. Кроме того, \G
он не реализован последовательно во всех разновидностях, которые его поддерживают. Этот трюк будет работать (например) в Java , Perl, .NET и JGSoft, но не в PHP (PCRE), Ruby 1.9+ или TextMate (оба Oniguruma). JavaScript /y
(липкий флаг) не такой гибкий \G
и не может использоваться таким образом, даже если JS действительно поддерживает поиск назад.
Следует упомянуть, что я не обязательно рекомендую это решение, если у вас есть другие варианты. Решения без регулярных выражений в других ответах могут быть длиннее, но они также самодокументируются; этот как раз противоположный этому. ;)
Кроме того, это не работает в Android, который не поддерживает использование \G
ретроспективного просмотра.
String.substring()
вместо регулярного выражения, хотя и требует нескольких дополнительных строк кода, будет работать где-то в 5 раз быстрее ...
(?s)
в регулярном выражении: (?s)(?<=\\G.{4})
.
java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Что ж, это довольно легко сделать с помощью простых арифметических и строковых операций:
public static List<String> splitEqually(String text, int size) {
// Give the list the right capacity to start with. You could use an array
// instead if you wanted.
List<String> ret = new ArrayList<String>((text.length() + size - 1) / size);
for (int start = 0; start < text.length(); start += size) {
ret.add(text.substring(start, Math.min(text.length(), start + size)));
}
return ret;
}
Я не думаю, что для этого действительно стоит использовать регулярное выражение.
РЕДАКТИРОВАТЬ: Мои доводы в пользу отказа от регулярного выражения:
Splitter.fixedLength(4)
как это было предложено seanizer.
С Google Guava это очень просто :
for(final String token :
Splitter
.fixedLength(4)
.split("Thequickbrownfoxjumps")){
System.out.println(token);
}
Вывод:
Theq
uick
brow
nfox
jump
s
Или, если вам нужен результат в виде массива, вы можете использовать этот код:
String[] tokens =
Iterables.toArray(
Splitter
.fixedLength(4)
.split("Thequickbrownfoxjumps"),
String.class
);
Ссылка:
Примечание. Конструкция сплиттера показана в строке выше, но поскольку сплиттеры неизменяемы и могут использоваться повторно, рекомендуется хранить их в константах:
private static final Splitter FOUR_LETTERS = Splitter.fixedLength(4);
// more code
for(final String token : FOUR_LETTERS.split("Thequickbrownfoxjumps")){
System.out.println(token);
}
String.join(separator, arrayOrCollection)
public static String[] split(String src, int len) {
String[] result = new String[(int)Math.ceil((double)src.length()/(double)len)];
for (int i=0; i<result.length; i++)
result[i] = src.substring(i*len, Math.min(src.length(), (i+1)*len));
return result;
}
src.length()
и len
оба являются int
s, ваш вызов ceiling
не выполняет то, что вы хотите - проверьте, как это делают некоторые другие ответы: (src.length () + len - 1) / len
public String[] splitInParts(String s, int partLength)
{
int len = s.length();
// Number of parts
int nparts = (len + partLength - 1) / partLength;
String parts[] = new String[nparts];
// Break into parts
int offset= 0;
int i = 0;
while (i < nparts)
{
parts[i] = s.substring(offset, Math.min(offset + partLength, len));
offset += partLength;
i++;
}
return parts;
}
for
петель?
for
Цикл действительно является более «естественным» использование выбора для этого :-) Спасибо за указание на это.
Вы можете использовать substring
from String.class
(обработка исключений) или из Apache lang commons (он обрабатывает исключения за вас)
static String substring(String str, int start, int end)
Поместите его в петлю, и все готово.
substring
методом в стандартном String
классе?
Я бы предпочел это простое решение:
String content = "Thequickbrownfoxjumps";
while(content.length() > 4) {
System.out.println(content.substring(0, 4));
content = content.substring(4);
}
System.out.println(content);
substring
реализации изменилось с Java 7, обновления 6 в середине 2012 года, когда offset
и count
поля были удалены из String
класса. Таким образом, сложность substring
превратилась в линейную задолго до того, как был дан ответ. Но для небольшой строки, такой как в примере, она по-прежнему выполняется достаточно быстро, а для более длинных строк ... ну, на практике эта задача встречается редко.
Вот однострочная реализация с использованием потоков Java8:
String input = "Thequickbrownfoxjumps";
final AtomicInteger atomicInteger = new AtomicInteger(0);
Collection<String> result = input.chars()
.mapToObj(c -> String.valueOf((char)c) )
.collect(Collectors.groupingBy(c -> atomicInteger.getAndIncrement() / 4
,Collectors.joining()))
.values();
Это дает следующий результат:
[Theq, uick, brow, nfox, jump, s]
String[] result = IntStream.range(0, (input.length()+3)/4) .mapToObj(i -> input.substring(i *= 4, Math.min(i + 4, input.length()))) .toArray(String[]::new);
Вот однострочная версия, которая использует Java 8 IntStream для определения индексов начала среза:
String x = "Thequickbrownfoxjumps";
String[] result = IntStream
.iterate(0, i -> i + 4)
.limit((int) Math.ceil(x.length() / 4.0))
.mapToObj(i ->
x.substring(i, Math.min(i + 4, x.length())
)
.toArray(String[]::new);
В случае , если вы хотите , чтобы разбить строку в равной степени в обратном направлении, то есть справа налево, например, разделить 1010001111
на [10, 1000, 1111]
, вот код:
/**
* @param s the string to be split
* @param subLen length of the equal-length substrings.
* @param backwards true if the splitting is from right to left, false otherwise
* @return an array of equal-length substrings
* @throws ArithmeticException: / by zero when subLen == 0
*/
public static String[] split(String s, int subLen, boolean backwards) {
assert s != null;
int groups = s.length() % subLen == 0 ? s.length() / subLen : s.length() / subLen + 1;
String[] strs = new String[groups];
if (backwards) {
for (int i = 0; i < groups; i++) {
int beginIndex = s.length() - subLen * (i + 1);
int endIndex = beginIndex + subLen;
if (beginIndex < 0)
beginIndex = 0;
strs[groups - i - 1] = s.substring(beginIndex, endIndex);
}
} else {
for (int i = 0; i < groups; i++) {
int beginIndex = subLen * i;
int endIndex = beginIndex + subLen;
if (endIndex > s.length())
endIndex = s.length();
strs[i] = s.substring(beginIndex, endIndex);
}
}
return strs;
}
Я использую следующее решение java 8:
public static List<String> splitString(final String string, final int chunkSize) {
final int numberOfChunks = (string.length() + chunkSize - 1) / chunkSize;
return IntStream.range(0, numberOfChunks)
.mapToObj(index -> string.substring(index * chunkSize, Math.min((index + 1) * chunkSize, string.length())))
.collect(toList());
}
Решение для Java 8 (как это, но немного проще):
public static List<String> partition(String string, int partSize) {
List<String> parts = IntStream.range(0, string.length() / partSize)
.mapToObj(i -> string.substring(i * partSize, (i + 1) * partSize))
.collect(toList());
if ((string.length() % partSize) != 0)
parts.add(string.substring(string.length() / partSize * partSize));
return parts;
}
Я спросил @Alan Moore в комментарии к принятому решению, как можно обрабатывать строки с новой строкой. Он предложил использовать ДОТАЛЛ.
Используя его предложение, я создал небольшой пример того, как это работает:
public void regexDotAllExample() throws UnsupportedEncodingException {
final String input = "The\nquick\nbrown\r\nfox\rjumps";
final String regex = "(?<=\\G.{4})";
Pattern splitByLengthPattern;
String[] split;
splitByLengthPattern = Pattern.compile(regex);
split = splitByLengthPattern.split(input);
System.out.println("---- Without DOTALL ----");
for (int i = 0; i < split.length; i++) {
byte[] s = split[i].getBytes("utf-8");
System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
}
/* Output is a single entry longer than the desired split size:
---- Without DOTALL ----
[Idx: 0, length: 26] - [B@17cdc4a5
*/
//DOTALL suggested in Alan Moores comment on SO: https://stackoverflow.com/a/3761521/1237974
splitByLengthPattern = Pattern.compile(regex, Pattern.DOTALL);
split = splitByLengthPattern.split(input);
System.out.println("---- With DOTALL ----");
for (int i = 0; i < split.length; i++) {
byte[] s = split[i].getBytes("utf-8");
System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
}
/* Output is as desired 7 entries with each entry having a max length of 4:
---- With DOTALL ----
[Idx: 0, length: 4] - [B@77b22abc
[Idx: 1, length: 4] - [B@5213da08
[Idx: 2, length: 4] - [B@154f6d51
[Idx: 3, length: 4] - [B@1191ebc5
[Idx: 4, length: 4] - [B@30ddb86
[Idx: 5, length: 4] - [B@2c73bfb
[Idx: 6, length: 2] - [B@6632dd29
*/
}
Но мне также нравится решение @Jon Skeets в https://stackoverflow.com/a/3760193/1237974 . Для удобства обслуживания в более крупных проектах, где не все одинаково знакомы с регулярными выражениями, я бы, вероятно, использовал решение Jons.
Еще одно решение для грубой силы:
String input = "thequickbrownfoxjumps";
int n = input.length()/4;
String[] num = new String[n];
for(int i = 0, x=0, y=4; i<n; i++){
num[i] = input.substring(x,y);
x += 4;
y += 4;
System.out.println(num[i]);
}
Где код просто перебирает строку с подстроками
import static java.lang.System.exit;
import java.util.Scanner;
import Java.util.Arrays.*;
public class string123 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("Enter String");
String r=sc.nextLine();
String[] s=new String[10];
int len=r.length();
System.out.println("Enter length Of Sub-string");
int l=sc.nextInt();
int last;
int f=0;
for(int i=0;;i++){
last=(f+l);
if((last)>=len) last=len;
s[i]=r.substring(f,last);
// System.out.println(s[i]);
if (last==len)break;
f=(f+l);
}
System.out.print(Arrays.tostring(s));
}}
результат
Enter String
Thequickbrownfoxjumps
Enter length Of Sub-string
4
["Theq","uick","brow","nfox","jump","s"]
@Test
public void regexSplit() {
String source = "Thequickbrownfoxjumps";
// define matcher, any char, min length 1, max length 4
Matcher matcher = Pattern.compile(".{1,4}").matcher(source);
List<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(source.substring(matcher.start(), matcher.end()));
}
String[] expected = {"Theq", "uick", "brow", "nfox", "jump", "s"};
assertArrayEquals(result.toArray(), expected);
}
Вот моя версия, основанная на потоках RegEx и Java 8. Стоит отметить, что этот Matcher.results()
метод доступен начиная с Java 9.
Тест включен.
public static List<String> splitString(String input, int splitSize) {
Matcher matcher = Pattern.compile("(?:(.{" + splitSize + "}))+?").matcher(input);
return matcher.results().map(MatchResult::group).collect(Collectors.toList());
}
@Test
public void shouldSplitStringToEqualLengthParts() {
String anyValidString = "Split me equally!";
String[] expectedTokens2 = {"Sp", "li", "t ", "me", " e", "qu", "al", "ly"};
String[] expectedTokens3 = {"Spl", "it ", "me ", "equ", "all"};
Assert.assertArrayEquals(expectedTokens2, splitString(anyValidString, 2).toArray());
Assert.assertArrayEquals(expectedTokens3, splitString(anyValidString, 3).toArray());
}
public static String[] split(String input, int length) throws IllegalArgumentException {
if(length == 0 || input == null)
return new String[0];
int lengthD = length * 2;
int size = input.length();
if(size == 0)
return new String[0];
int rep = (int) Math.ceil(size * 1d / length);
ByteArrayInputStream stream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_16LE));
String[] out = new String[rep];
byte[] buf = new byte[lengthD];
int d = 0;
for (int i = 0; i < rep; i++) {
try {
d = stream.read(buf);
} catch (IOException e) {
e.printStackTrace();
}
if(d != lengthD)
{
out[i] = new String(buf,0,d, StandardCharsets.UTF_16LE);
continue;
}
out[i] = new String(buf, StandardCharsets.UTF_16LE);
}
return out;
}
public static List<String> getSplittedString(String stringtoSplit,
int length) {
List<String> returnStringList = new ArrayList<String>(
(stringtoSplit.length() + length - 1) / length);
for (int start = 0; start < stringtoSplit.length(); start += length) {
returnStringList.add(stringtoSplit.substring(start,
Math.min(stringtoSplit.length(), start + length)));
}
return returnStringList;
}