Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

This seems like it should be trivial, but how do I partition a string into length n substrings? I can of course write something like

chunk[s_, n_] := StringJoin[#] & /@ Partition[Characters[s], n]

so that chunk["ABCDEF",2] -> {"AB","CD","EF"} but this appears unnecessarily cumbersome.

share|improve this question
    
Welcome to Mathematica.SE! This is a good question, as there doesn't seem to be a direct built-in way (I bumped into this before). Please consider filling out the name field in your profile, so it will show as something easier to remember than 'user1268' –  Szabolcs May 17 '12 at 9:19
    
Link to MathGroup version. –  Szabolcs May 17 '12 at 10:22
    
Note StringJoin[#] & is the same as just StringJoin. –  amr Oct 3 '12 at 19:59

6 Answers 6

up vote 18 down vote accepted

Try this:

StringCases["ABCDEFGHIJK", LetterCharacter ~~ LetterCharacter]

{"AB", "CD", "EF", "GH", "IJ"}

or for more general cases (i.e. not just for letters, but any characters, and for any partition size):

stringPartition1[s_String, n_Integer] := StringCases[s, StringExpression @@ Table[_, {n}]];

It is more elegant though to use Repeated (thanks rcollyer):

stringPartition2[s_String, n_Integer] := StringCases[s, Repeated[_, {n}]];

stringPartition2["longteststring", 4]

{"long", "test", "stri"}

share|improve this answer
7  
Instead of Table[_,{n}], I'd consider using Repeated[_, {n}], instead. –  rcollyer May 17 '12 at 1:34
    
Thanks @rcollyer, I felt I missed something, but it was too late. Incorporated now. –  István Zachar May 17 '12 at 9:50

Here is the regular-expression way:

chunk[s_, n_] := 
 StringCases[s, RegularExpression[".{1," <> ToString[n] <> "}"]]

chunk["Hello this is a test string", 2]

{"He", "ll", "o ", "th", "is", " i", "s ", "a ", "te", "st", " s", "tr", "in", "g"}

chunk["Hello this is a test string", 4]

{"Hell", "o th", "is i", "s a ", "test", " str", "ing"}

Note that the last substrings didn't fit the chunk size but were still included.

If you don't want to include them, change the regular expression from ".{1," <> ToString[n] <> "}" to ".{" <> ToString[n] <> "}".

share|improve this answer

Another possibility:

StringTake[#, 
   Partition[Range@StringLength@#, 2, 2, 1, {}]] &@"abcdefghi"

giving

(*  {"ab", "cd", "ef", "gh", "i"} *)
share|improve this answer

This will give better performance (3 times faster in my test, partitioning into length-two strings) than your original code:

chunk[s_, n_] := FromCharacterCode@Partition[ToCharacterCode[s], n]

The reason is that the first few steps of the computation are done with packed arrays.

It will still be slower than the regex-based approaches (István's and Jens's), on my machine by a factor of 2.

The StringTake approach is much slower than all the others in my machine.

Benchmarks

Function definitions:

(* original *)
chunk1[s_, n_] := StringJoin[#] & /@ Partition[Characters[s], n]

(* István *)
chunk2[s_, n_] := StringCases[s, Repeated[_, {n}]]

(* Jens *)
chunk3[s_, n_] := StringCases[s, RegularExpression[".{1," <> ToString[n] <> "}"]]

(* TomD *)
chunk4 = StringTake[#, Partition[Range@StringLength@#, #2, #2, 1, {}]] &;

(* mine *)
chunk5[s_, n_] := FromCharacterCode@Partition[ToCharacterCode[s], n]

text = ExampleData[{"Text", "Hamlet"}];
testString = StringJoin[ConstantArray[text, 20]];

StringLength[testString] (* 3438740 *)

Timings:

(* original *)
In[10]:= Timing[chunk1[testString, 2];]
         Timing[chunk1[testString, 100];]

Out[10]= {5.968, Null}
Out[11]= {1.703, Null}

(* István - fastest *)
In[12]:= Timing[chunk2[testString, 2];]
         Timing[chunk2[testString, 100];]

Out[12]={1.25, Null}
Out[13]={0.11, Null}

(* Jens - fastest *)
In[14]:= Timing[chunk3[testString, 2];]
         Timing[chunk3[testString, 100];]

Out[14]= {1.313, Null}
Out[15]= {0.125, Null}

(* TomD *)
In[16]:= Timing[chunk4[testString, 2];]
         Timing[chunk4[testString, 100];]

(* More than a few minutes. Didn't wait for it to finish ... *)

(* mine *)
In[18]:= Timing[chunk5[testString, 2];]
         Timing[chunk5[testString, 100];]

Out[18]= {2.25, Null}
Out[19]= {0.266, Null}

Conclusion: use regex-based methods. The built-in string patterns also use a regex library internally, I believe, but they are easier to construct programmatically because they are represented as expressions.

share|improve this answer
    
Scrollbar ate your timing results... First I thought it was so lightning fast you did not bother to write it out in numbers :) –  István Zachar May 17 '12 at 9:54
1  
Thanks, Szabolcs, and everyone who answered. As ever, more than one way to skin a cat with Mathematica. I'm a bit surprised that there isn't a StringPartition function taking the same arguments as Partition as a built-in, analogous to StringTake vs. Take. –  David G May 17 '12 at 13:23
2  
Your code can be further sped up by using Developer`PartitionMap to apply FromCharacterCode to each term in the list. This does not unpack the list. The speed up is marginal, though, on my machine 1.25793 -> 1.12617 and 0.135588 -> 0.095252. So, still not a contender for the fastest method. –  rcollyer May 17 '12 at 16:38
1  
@rcollyer Good point! I never used Developer`PartitionMap before. (I've seen it in the docs, but I didn't realize its significance: not unpacking.) –  Szabolcs May 17 '12 at 16:40
1  
@rcollyer I solved the 'mystery': it was auto-compilation. It avoids unpacking only if the list is above the auto-compilation length. –  Szabolcs May 17 '12 at 17:08

I wanted a solution using StringSplit[]

chunk[s_,n_]:=StringSplit[s,RegularExpression["(.{"<>ToString@n<>"})"]->"$1"]~DeleteCases~""
share|improve this answer
    
Cleaner, IMO: StringSplit[s, x : Repeated[_, {n}] :> x][[;; ;; 2]]. I don't see an advantage over StringCases however. –  Mr.Wizard May 4 at 11:05
    
@Mr.Wizard No advantage. I just wanted a way to use StringSplit[] as said. But I'm too old to remember why I wanted that. –  belisarius May 5 at 3:49

Not completely orginal, but very compact.

chunk[s_, n_] := StringJoin@@@Partition[Characters[s], n, n, 1, {}]
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.