Comment diviser la chaîne "Thequickbrownfoxjumps"
en sous-chaînes de taille égale en Java . "Thequickbrownfoxjumps"
de 4 taille égale devrait donner la sortie.
["Theq","uick","brow","nfox","jump","s"]
Question similaire:
Fractionner la chaîne en sous-chaînes de longueur égale dans Scala
Voici la version regex one-liner:
System.out.println(Arrays.toString(
"Thequickbrownfoxjumps".split("(?<=\\G.{4})")
));
\G
est une assertion de largeur zéro qui correspond à la position où la correspondance précédente s'est terminée. S'il n'y a pas de correspondance précédente était, le début de l'entrée est identique, à l'identique de \A
. La ligne de visée ci-dessous correspond à la position de quatre caractères à partir de la fin du dernier match.
Lookbehind et \G
sont des fonctionnalités regex avancées, qui ne sont pas prises en charge par toutes les variantes. De plus, \G
n'est pas implémenté de manière cohérente dans les variantes qui le prennent en charge. Cette astuce fonctionnera (par exemple) dans Java , Perl, .NET et JGSoft, mais pas dans PHP (PCRE), Ruby 1.9+ ou TextMate (Oniguruma). /y
(indicateur collant) de JavaScript n'est pas aussi flexible que \G
et ne peut pas être utilisé de cette manière même si JS prenait en charge lookbehind.
Je devrais mentionner que je ne fais pas nécessairement recommander cette solution si vous avez d'autres options. Les solutions autres que les expressions rationnelles dans les autres réponses peuvent être plus longues, mais elles s'auto-documentent également; celui-ci est à peu près le opposé de cela. ;)
En outre, cela ne fonctionne pas sous Android, qui ne prend pas en charge l'utilisation de \G
dans les lookbehind.
Eh bien, c'est assez facile de le faire par force brute:
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;
}
Je ne pense pas que cela vaut vraiment la peine d'utiliser une regex pour cela.
EDIT: Mon raisonnement pour ne pas utiliser une regex:
C'est très facile avec Google Guava :
for(final String token :
Splitter
.fixedLength(4)
.split("Thequickbrownfoxjumps")){
System.out.println(token);
}
Sortie:
Theq
uick
brow
nfox
jump
s
Ou si vous avez besoin du résultat sous forme de tableau, vous pouvez utiliser ce code:
String[] tokens =
Iterables.toArray(
Splitter
.fixedLength(4)
.split("Thequickbrownfoxjumps"),
String.class
);
Référence:
Remarque: La structure des séparateurs est illustrée ci-dessus, mais comme les séparateurs sont immuables et réutilisables, il est recommandé de les stocker dans des constantes:
private static final Splitter FOUR_LETTERS = Splitter.fixedLength(4);
// more code
for(final String token : FOUR_LETTERS.split("Thequickbrownfoxjumps")){
System.out.println(token);
}
Si vous utilisez les bibliothèques généralistes de guava de Google (et très honnêtement, tout nouveau projet Java probablement devrait être), ceci est incroyablement trivial avec la classe Splitter :
for (String substring : Splitter.fixedLength(4).split(inputString)) {
doSomethingWith(substring);
}
et c'est ça. Facile comme!
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;
}
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;
}
Vous pouvez utiliser substring
depuis String.class
(gestion des exceptions) ou depuis Apache lang commons (il gère les exceptions pour vous).
static String substring(String str, int start, int end)
Mettez-le dans une boucle et vous êtes prêt à partir.
Voici une implémentation one-liner utilisant des flux 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();
Il donne la sortie suivante:
[Theq, uick, brow, nfox, jump, s]
Voici une version one-liner qui utilise Java 8 IntStream pour déterminer les index des débuts de tranche:
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);
Je préfère cette solution simple:
String content = "Thequickbrownfoxjumps";
while(content.length() > 4) {
System.out.println(content.substring(0, 4));
content = content.substring(4);
}
System.out.println(content);
Si vous souhaitez scinder la chaîne de manière égale vers l’arrière, c’est-à-dire de droite à gauche, par exemple, pour scinder 1010001111
en [10, 1000, 1111]
, le code est le suivant:
/**
* @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;
}
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;
}
Voici ma version basée sur les flux RegEx et Java 8. Il convient de mentionner que la méthode Matcher.results()
est disponible depuis Java 9.
Test inclus.
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());
}
Une autre solution de force brute pourrait être,
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]);
}
Où le code passe juste à travers la chaîne avec des sous-chaînes
Solution Java 8 (comme ceci mais un peu plus simple):
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;
}
j'utilise la solution Java 8 suivante:
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());
}
@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);
}
J'ai demandé à @Alan Moore dans un commentaire sur la solution acceptée comment les chaînes contenant des nouvelles lignes pourraient être traitées. Il a suggéré d'utiliser DOTALL.
En utilisant sa suggestion, j'ai créé un petit échantillon de la façon dont cela fonctionne:
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
*/
}
Mais j'aime bien la solution @Jon Skeets dans https://stackoverflow.com/a/3760193/1237974 aussi. Pour la maintenabilité dans les grands projets où tous ne sont pas également expérimentés dans les expressions régulières, j'utiliserais probablement la solution de Jons.
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));
}}
Résultat
Enter String
Thequickbrownfoxjumps
Enter length Of Sub-string
4
["Theq","uick","brow","nfox","jump","s"]