在第n个字符处分割字符串


78

在JavaScript中,这是我们如何在每个第3个字符处分割字符串

"foobarspam".match(/.{1,3}/g)

我试图弄清楚如何用Java做到这一点。有指针吗?


我不会将Regex用于此任务。
kennytm 2010年

3
好。那你有什么建议?
Vijay Dev'2

1
就像西蒙的答案。
kennytm 2010年

我支持你的建议。无需安装其他库,Simon的解决方案效果很好。
哈珀维尔

Answers:


131

您可以这样做:

String s = "1234567890";
System.out.println(java.util.Arrays.toString(s.split("(?<=\\G...)")));

产生:

[123, 456, 789, 0]

正则表达式(?<=\G...)匹配一个空字符串,该字符串的最后一个匹配项\G)后跟三个字符...前者((?<= )


15
我不希望有人因为不喜欢正则表达式而否决了这个答案。
威廉·布伦德尔2010年

55
最高正则表达式mojo的疯狂道具,但作为对此代码的读者,我会追捕您并为您的房子添砖加瓦。:)
Kevin Bourrillion 2010年

4
只要您通过正确命名的函数(即splitIntoParts)调用此函数,并且不直接在代码中嵌入该行,就可以了。否则,让狩猎开始:)
GreenieMeanie 2010年

3
使得此技巧如此可怕的部分原因在于,它并非在所有语言中都有效。例如,JavaScript不支持\G,Python不会在匹配零个字符的正则表达式上拆分。但是,如果Java与其他所有语言一样都具有“获取所有匹配项”的方法,则您不必首先发明@Bart这个技巧。;)
艾伦·摩尔

7
我将此复制/粘贴到我的Android Studio项目中,[123, 4567890]结果如下:(
Evren Yurtesen

83

Java没有提供功能非常齐全的拆分工具,因此Guava库提供了

Iterable<String> pieces = Splitter.fixedLength(3).split(string);

查阅Javadoc for Splitter ; 它非常强大。


7
+1这是正确的答案(也称为:知道并使用库
Jonik

4
我会用正则表达式来回答这个问题……只是因为它更易于维护(例如,人们对RegEx的了解少于ppl能够读取“可读”代码的人。)
sivabudh 2010年

4
仅当您已经具有Guava依赖项时才行。否则,您需要添加另一个依赖项-如果不先与同事/系统架构师联系,则不应这样做。
foo

1
在大多数情况下,添加完整的库以便仅使用一种方法并不是最佳实践,而且在企业环境中添加库始终是一个重大决定。
GaboSampaio

50
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        for (String part : getParts("foobarspam", 3)) {
            System.out.println(part);
        }
    }
    private static List<String> getParts(String string, int partitionSize) {
        List<String> parts = new ArrayList<String>();
        int len = string.length();
        for (int i=0; i<len; i+=partitionSize)
        {
            parts.add(string.substring(i, Math.min(len, i + partitionSize)));
        }
        return parts;
    }
}

如果保留覆盖整个原始字符串的子字符串集合,则新的String方法实际上将浪费(n-1)* sizeof(int)。新的Strings char数组将占用相同的内存,但是每个数组将具有一个单独的length字段。也就是说,如果以后再丢弃任何子字符串,则新的String可能会减少内存。除非原始字符串很大,否则我都不会担心。
ILMTitan 2010年

@DenisTulskiy您能详细说明吗?该substring方法实际上很聪明,可以使用父字符串char[]作为数据。有关更多详细信息,请参见此答案
wchargin 2013年

1
@WChargin:嗯,你是对的,我不知道为什么要写这个评论。我将其删除。谢谢。
Denis Tulskiy

7

作为Bart Kiers回答的补充,我想补充一点,有可能代替使用...正则表达式中的三个点来表示三个可以书写的.{3}具有相同含义的字符。

然后,代码如下所示:

String bitstream = "00101010001001010100101010100101010101001010100001010101010010101";
System.out.println(java.util.Arrays.toString(bitstream.split("(?<=\\G.{3})")));

这样一来,修改字符串长度将变得更加容易,并且现在可以使用可变的输入字符串长度来创建函数了。可以完成以下操作:

public static String[] splitAfterNChars(String input, int splitLen){
    return input.split(String.format("(?<=\\G.{%1$d})", splitLen));
}

IdeOne中的一个示例:http ://ideone.com/rNlTj5


3

迟到。

以下是使用Java8流和一个衬里的简洁实现:

String foobarspam = "foobarspam";
AtomicInteger splitCounter = new AtomicInteger(0);
Collection<String> splittedStrings = foobarspam
                                    .chars()
                                    .mapToObj(_char -> String.valueOf((char)_char))
                                    .collect(Collectors.groupingBy(stringChar -> splitCounter.getAndIncrement() / 3
                                                                ,Collectors.joining()))
                                    .values();

输出:

[foo, bar, spa, m]

5
“一个班轮”;)
克里斯(Chris

1

这是一个很晚的答案,但是无论如何我都会把它发布给任何新程序员看看:

如果您不想使用正则表达式,并且不希望依赖第三方库,则可以改用此方法,在2.80 GHz CPU(不到一毫秒)中,此方法花费 89920100113纳秒。它不像西蒙·尼克森(Simon Nickerson)的例子那么漂亮,但是可以起作用:

   /**
     * Divides the given string into substrings each consisting of the provided
     * length(s).
     * 
     * @param string
     *            the string to split.
     * @param defaultLength
     *            the default length used for any extra substrings. If set to
     *            <code>0</code>, the last substring will start at the sum of
     *            <code>lengths</code> and end at the end of <code>string</code>.
     * @param lengths
     *            the lengths of each substring in order. If any substring is not
     *            provided a length, it will use <code>defaultLength</code>.
     * @return the array of strings computed by splitting this string into the given
     *         substring lengths.
     */
    public static String[] divideString(String string, int defaultLength, int... lengths) {
        java.util.ArrayList<String> parts = new java.util.ArrayList<String>();

        if (lengths.length == 0) {
            parts.add(string.substring(0, defaultLength));
            string = string.substring(defaultLength);
            while (string.length() > 0) {
                if (string.length() < defaultLength) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        } else {
            for (int i = 0, temp; i < lengths.length; i++) {
                temp = lengths[i];
                if (string.length() < temp) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, temp));
                string = string.substring(temp);
            }
            while (string.length() > 0) {
                if (string.length() < defaultLength || defaultLength <= 0) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        }

        return parts.toArray(new String[parts.size()]);
    }

1

使用纯Java:

    String s = "1234567890";
    List<String> list = new Scanner(s).findAll("...").map(MatchResult::group).collect(Collectors.toList());
    System.out.printf("%s%n", list);

产生输出:

[123、456、789]

请注意,这将丢弃剩余字符(在这种情况下为0)。


0

您还可以在第n个字符处分割一个字符串,并将每个字符串放在List的每个索引中:

在这里,我列出了一个名为Sequence的字符串列表:

列表<字符串>序列

然后,我基本上将字符串“ KILOSO”每2个字分割一次。因此,“ KI”,“ LO”,“ SO”将合并到称为“序列”的列表的单独索引中。

字符串S = KILOSO

序列= Arrays.asList(S.split(“(?<= \ G ..)”)));

所以当我在做:

System.out.print(序列)

它应该打印:

[KI,LO,SO]

验证我可以写:

System.out.print(Sequence.get(1))

它将打印:

LO


0

我最近遇到了这个问题,这是我想出的解决方案

final int LENGTH = 10;
String test = "Here is a very long description, it is going to be past 10";

Map<Integer,StringBuilder> stringBuilderMap = new HashMap<>();
for ( int i = 0; i < test.length(); i++ ) {
    int position = i / LENGTH; // i<10 then 0, 10<=i<19 then 1, 20<=i<30 then 2, etc.

    StringBuilder currentSb = stringBuilderMap.computeIfAbsent( position, pos -> new StringBuilder() ); // find sb, or create one if not present
    currentSb.append( test.charAt( i ) ); // add the current char to our sb
}

List<String> comments = stringBuilderMap.entrySet().stream()
        .sorted( Comparator.comparing( Map.Entry::getKey ) )
        .map( entrySet -> entrySet.getValue().toString() )
        .collect( Collectors.toList() );
//done



// here you can see the data
comments.forEach( cmt -> System.out.println( String.format( "'%s' ... length= %d", cmt, cmt.length() ) ) );
// PRINTS:
// 'Here is a ' ... length= 10
// 'very long ' ... length= 10
// 'descriptio' ... length= 10
// 'n, it is g' ... length= 10
// 'oing to be' ... length= 10
// ' past 10' ... length= 8

// make sure they are equal
String joinedString = String.join( "", comments );
System.out.println( "\nOriginal strings are equal " + joinedString.equals( test ) );
// PRINTS: Original strings are equal true
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.