在每个第 n 个字符处拆分一个字符串

Posted

技术标签:

【中文标题】在每个第 n 个字符处拆分一个字符串【英文标题】:Splitting a string at every n-th character 【发布时间】:2011-01-18 20:01:18 【问题描述】:

javascript 中,这就是我们如何在每个第三个字符处分割一个字符串

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

我试图弄清楚如何在 Java 中做到这一点。有什么指点吗?

【问题讨论】:

我不会在这个任务中使用正则表达式。 好的。那你有什么建议呢? 类似于西蒙的回答。 我支持您的建议。无需安装额外的库,Simon 的解决方案效果很好。 【参考方案1】:
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)。新字符串的 char 数组将占用相同的内存,但每个都有一个单独的长度字段。也就是说,如果以后丢弃任何子字符串,则 new String 可能会减少内存。除非原始字符串很大,否则我不会担心。 @DenisTulskiy 你能详细说明一下吗? substring 方法实际上足够聪明,可以将父字符串的char[] 用于数据;有关详细信息,请参阅this answer。 @WChargin:嗯,你说得对,我不知道我为什么要写那条评论。我会删除它。谢谢。 我会说这个答案和正则表达式一样正确。【参考方案2】:

你可以这样做:

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

产生:

[123, 456, 789, 0]

正则表达式(?&lt;=\G...) 匹配具有最后一个匹配项 (\G) 后跟三个字符 (...) 之前它((?&lt;= )

【讨论】:

我不想认为有人仅仅因为他们不喜欢正则表达式而否决了这个答案。 最高正则表达式 mojo 的疯狂道具,但作为此代码的读者,我会追捕你并为你的房子加油。 :) 只要您通过正确命名的函数(即 splitIntoParts)调用它并且不直接将该行嵌入代码中,一切都很好。否则,让狩猎开始吧:) 这个技巧如此可怕的部分原因在于它不适用于所有语言。例如,JavaScript 不支持\G,Python 不会在匹配零个字符的正则表达式上进行拆分。但是,如果 Java 有像其他所有语言一样的“获取所有匹配项”方法,那么您就不必首先发明这个技巧,@Bart。 ;) 我将其复制/粘贴到我的 android Studio 项目中,结果得到[123, 4567890] :(【参考方案3】:

Java 没有提供非常全功能的拆分实用程序,所以 Guava libraries 这样做:

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

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

【讨论】:

+1 这是正确答案(也称为:知道并使用库 我会接受这个答案而不是正则表达式......只是因为它更易于维护(例如,与能够阅读“可读”代码的人相比,了解 RegEx 的人更少。) 只有当你已经有 Guava 依赖时才好。否则,您需要添加另一个依赖项 - 如果不先与同事/系统架构师核实,您不应该这样做。 添加一个完整的库以便您可以只使用一种方法在大多数情况下并不是最佳实践,而且在企业环境中添加库始终是一个重大决策。【参考方案4】:

作为对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

【讨论】:

【参考方案5】:

这是一个迟到的答案,但我还是把它放在那里让任何新程序员都能看到:

如果你不想使用正则表达式,不想依赖第三方库,你可以改用这个方法,它需要介于 89920100113 纳秒(在 2.80 GHz CPU 中)(不到一毫秒)。它不像西蒙尼克森的例子那么漂亮,但它确实有效:

   /**
     * 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()]);
    

【讨论】:

【参考方案6】:

迟到。

以下是使用 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]

【讨论】:

“单排” ;)【参考方案7】:

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

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

列表序列

然后我基本上将字符串“KILOSO”按每 2 个单词进行拆分。所以“KI”“LO”“SO”将被合并到称为序列的列表的单独索引中。

字符串 S = KILOSO

Sequence = Arrays.asList(S.split("(?

所以当我在做的时候:

System.out.print(序列)

应该打印出来:

[KI、LO、SO]

验证我可以写:

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

它会打印出来:

LO

【讨论】:

【参考方案8】:

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

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

【讨论】:

【参考方案9】:

使用纯 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)。

【讨论】:

【参考方案10】:

我会从类似的东西开始

public List<String> split(String str, int interval) 
    if (str.length() <= interval) 
        return List.of(str);
    
    var subStrings = new ArrayList<String>();
    int pointer = 0;
    while (str.length() > pointer) 
        String substring = str.substring(pointer, pointer + interval);
        subStrings.add(substring);
        pointer += interval;
    
    return subStrings;

【讨论】:

以上是关于在每个第 n 个字符处拆分一个字符串的主要内容,如果未能解决你的问题,请参考以下文章

如果字符串包含多个 \n,如何在每 25 个换行符(\n)上拆分一个字符串

R 使用 tidyr::separate 在最后一个空格字符处拆分字符串

动态规划之字符串拆分

如何在第一个`/`(斜杠)处拆分字符串并在`<span>`中包围它的一部分?

在特定字符处拆分字符串但忽略某些情况 C# LINQ

在每 N 个字符处向字符串添加分隔符?