How to alternate a string with a sequence of characters

What is the best way to alternate a java string with a given character sequence. The rotation interval must be variable.

Example:

String s = " .... 0000000000000 ..."; // length random String b = interleave(s, 3, "-"); 

Result:

 ... 000-000-000-000-000 ... 

another example:

 String s = " .... we all we all we all ..."; String b = interleave(s, 7, "rock "); 

Result:

 ... we all rock we all rock we all rock ... 

The function should also work if the string length is not a multiple of the distance between the channels. Any suggestions? Is there a (again) “public” way to do this?

+7
java string
source share
5 answers

Here's a pretty simple and pretty readable implementation (I call it StringBuilder in the test below):

 public static String interleave(String s, int interval, String separator) { StringBuilder sb = new StringBuilder(s); for (int pos = (s.length()-1) / interval; pos > 0; pos--) { sb.insert(pos * interval, separator); } return sb.toString(); } 

If you are interested in the effectiveness of a simple StringBuilder implementation, then perhaps this implementation will suit your needs better (I call it Arrays in the test example below):

 public static String interleave(String string, int interval, String separator) { char[] src = string.toCharArray(); char[] sep = separator.toCharArray(); int count = (src.length-1)/interval; char[] dst = new char[src.length + count * sep.length]; int srcpos = 0, dstpos = 0; for (int i = 0; i < count; i++) { System.arraycopy(src, srcpos, dst, dstpos, interval); srcpos += interval; dstpos += interval; System.arraycopy(sep, 0, dst, dstpos, sep.length); dstpos += sep.length; } if (dstpos < dst.length) { System.arraycopy(src, srcpos, dst, dstpos, dst.length - dstpos); } return String.valueOf(dst); } 

Note. I would probably use such an implementation only in the J2ME environment, but it should be much faster on huge lines. The readability is pretty poor though ...

Of course, there is always a way for RegExp to do things that are surprisingly pretty fast after you climbed in length, when compiling RegExp itself ceases to be a problem (you cannot precompile RegExp because it is generated to fly depending on the interval, thanks Rubens Farias for pointing this out somehow missed him). So here it is (I call this RegExp in the following test case):

 public static String interleave(String string, int interval, String separator) { return string.replaceAll("(.{"+interval+"})", "$1"+Matcher.quoteReplacement(separator)); } 

Note. . This implementation inserts a delimiter at the end if the string length is a multiple of the spacing (while other implementations are missing). I do not like RegExps because they are not readable and not too fast. Oh, and you can easily forget the "quoteReplacement" part and put yourself in a big problem if the delimiter contains "1 dollar" or even worse if it comes from the user.

Benchmark

At this moment, I did some benchmarking, so the first implementation on a line length of 100,000 takes 0.002643 seconds, the second - 0.000010, the third - 0.000071, but it all depends on the length of the line.

 Length StringBuilder Arrays RegExp 10000 0.000012 0.000001 0.000054 100000 0.002643 0.000010 0.000071 1000000 0.315413 0.000026 0.000199 

This is by no means a serious benchmarking, but it still shows the trends and complexities of the algorithms used.

Note. . Although these ideas are fun to play with, we are still talking about improvements for the second part when dealing with strings smaller than 1 MB. Therefore, it doesn’t matter where you go if you work only with lines up to 1K in size (this will be 0ms versus 0ms). The most important thing is that it should be readable, understandable and not take too much time to write, because I am sure that you have more important problems to solve if you are not writing a universal library for everyone to use in the most strange cases . Remember - your time is much more valuable than processor time.

Left and Right Interleave

I will take the implementation of arrays for this, since for me it is easiest to change:

 public static String interleave(String string, int interval, String separator, boolean fromRight) { char[] src = string.toCharArray(); char[] sep = separator.toCharArray(); int count = (src.length-1)/interval; char[] dst = new char[src.length + count * sep.length]; int srcpos = 0, dstpos = 0; if (fromRight) { srcpos = dstpos = src.length - count * interval; if (srcpos > 0) System.arraycopy(src, 0, dst, 0, srcpos); if (count > 0) { System.arraycopy(sep, 0, dst, dstpos, sep.length); dstpos += sep.length; count--; } } for (int i = 0; i < count; i++) { System.arraycopy(src, srcpos, dst, dstpos, interval); srcpos += interval; dstpos += interval; System.arraycopy(sep, 0, dst, dstpos, sep.length); dstpos += sep.length; } if (dstpos < dst.length) { System.arraycopy(src, srcpos, dst, dstpos, dst.length - dstpos); } return String.valueOf(dst); } 
+14
source share

I think this solution is very effective. Does not require copying an array or StringBuilder extension:

 public static String interleave(String input, int interval, String sep) { StringBuilder sb = new StringBuilder(input.length() + (((input.length() -1) / interval) * sep.length())); char[] array = input.toCharArray(); for (int i = 0; i < array.length; i += interval) { int span = i + interval; for (int j = i; j < Math.min(span, array.length); j++) { sb.append(array[j]); } if (span < array.length) sb.append(sep); } return sb.toString(); } 
+1
source share

This is C #, but I'm sure Java has a similar approach:

 public static string interleave(string input, int interval, string separator) { if (String.IsNullOrEmpty(input) || String.IsNullOrEmpty(separator) || interval <= 0) return input; int length = input.Length + // original length + added chars - last occur ((input.Length / interval) * separator.Length) - (input.Length % interval == 0 ? separator.Length : 0); return Regex.Replace( // magic happens here input, String.Format("(.{{{0}}})", interval), "$1" + separator.Replace("$", "$$")).Substring(0, length); } 
+1
source share

This is efficient and clear:

 public static String interleave(String s, int interval, String separator) { StringBuffer b = new StringBuffer(); int length = s.length(); for (int start = 0; start < length - 1; start += interval) { int end = Math.min(length, start + interval); b.append(s.substring(start, end)); b.append(separator); } if (length % interval > 0) { b.append(s.substring(length - (length % interval))); } return b.toString(); } 
0
source share

Using the Google Guava Preliminary Libraries :

 Joiner.on("-").join(Splitter.fixedLength(3).split(inputString)); 

Short, clear and expressive. Love him!

0
source share

All Articles