tags:

views:

386

answers:

9

I was wondering how can I add a newline character (i.e. /n or <br>) after X number of characters.

For example, let's say I have a perl variable $message ="aaaaabbbbbcccccdd". I want to add a newline character after every 5 characters to this variable. So when I print the variable in html it will display:

aaaaa 
bbbbb 
ccccc 
dd

What is the best way to do this? I was told to use substr or a count function, but I'm not sure how to go about it. Any help will be greatly appreciated. Thanks!

A: 
echo 123456789abcd|perl -ne'print "$1\n" while s/(^.{5})//;print;'
catwalk
See other ".{5}" regex answers; you will drop leftovers.
Robert P
Bonus points for using a one-liner :) I'm always surprised at how many people don't know about perl -ne. (See "perldoc perlrun")
Ether
A: 
substr($string, 0, 5);

Couple that with some variables:

$x = 0;
$newstring = '';
while(length($string)<$x){
    $newstring = $newstring + substr($string, $x, ($x+5)) + '\n';
    $x = $x + 5;
}
Satanicpuppy
1. Addition is a mathematical operation in Perl, even on strings; you want the string concatenation operator '.'.2. The 3rd argument to substr is the length of the substring, not the offset of the end of the substring.3. Newline has to be in double quotes: "\n"; '\n' is the literal string \n.4. The test in your while loop is backwards; it should be while(length($string)>$x).
nohat
+2  A: 

In perl, there are many ways to accomplish the same thing ;-)

One them might be:

$message = "aaaaabbbbbcccccdd";
$splitmessage = join ("\n",  ( $message =~ /.{1,5}/gs ));
print $splitmessage, "\n";

Output:

aaaaa
bbbbb
ccccc
dd
Andre Miller
Any reason for the downvote?
Andre Miller
A: 

Just pass your string through this regexp:

=~ s/([^\n]{5})/$1\n/g

and you should be fine.

If what you really have is not a random string of random characters, but a text - you might want to use Text::Wrap module instead.

depesz
the {5} is incorrect; this will drop any leftover characters.
Robert P
@RobertP - what do you mean by "drop"? echo "123456789" | perl -pe 's/([^\n]{5})/$1\n/g'shows correct result.
depesz
@RobertP - no, it does not drop the leftovers, because it does not match :-D
Massa
It won't add the final \n if it doesn't match them; he wanted a new line at the end of every set of characters.
Robert P
@RobertP: quote: "add a newline character after every 5 characters" - it doesn't say anything about adding new line character after last 2 or 3 characters if the number of characters is not divisible by 5.
depesz
Taken from his example, his last line with only two d's has a <br> after it. I took it to mean that he wants to add something at the end of them, even if it's less than 5. Throwing the <br> in there and trying it out locally, I got aaaaa<br>bbbbb<br>ccccc<br>dd
Robert P
+3  A: 

I heard that the most efficient way is to use unpack:

say for unpack "(A5)*", "012345678901234567890123456879"

Output:

01234
56789
01234
56789
01234
56879
Massa
That's an interesting use of unpack. I like it. It's a lot cleaner than a regex version.
Robert P
Except it doesn't add it to the variable. You'd need a slight change to make that work.
Robert P
if you want to add the `<br>`s to the variable, you must do something like $x = join " <br>\n", unpack '(A5)*', $x
Massa
+4  A: 

An even shorter option.

$m = "aaaaabbbbbcccccdd";
$m =~ s/(.{1,5})/$1\n/gs;
print $m;

Outputs:

aaaaa
bbbbb
ccccc
dd

Of course I think my version is the best of all presented up to now. ;)

why (.{1,5}) and not (.{5})?
roddik
Because otherwise it won't capture leftover parts; eg, the dd will be left out because it doesn't match .{5}.
Robert P
@roddick: you need to get the last group, which might not be five characters.
brian d foy
thank you! this solution works for me.
qdog
@RobertP - you can easily go with .{5} - .{1,5} is *not* necessary!
depesz
If you run the code with just .{5}, it *almost* works. It will not add the last bit of text if it is not exactly 5 characters, and he wanted newlines on each line.
Robert P
using .{5} works for me. Initially, I had dd <br> as the output but I don't need the newline if the leftover characters are less than 5. I just edited my output to avoid any confusion. Thank you all for your contributions.
qdog
+2  A: 

Building on Massa's answer, I'd do it like this:

$message = join("\n", unpack('(A5)*', $message ))

running it,

$ perl
use strict;
use warnings;

my $message = "aaaaabbbbbcccccdd";

$message = join("\n", unpack("(A5)*", $message));
print $message;
^D
aaaaa
bbbbb
ccccc
dd

Replace "\n" with whatever you want to actually terminate each line with (eg, "\<br>\n" .)

Robert P
A: 

Since it appears you're trying to wrap text, I'd look at something like Text::Wrap

mpeters
A: 

The unpack method is probably the most efficient, if a bit obtuse. The regex method is probably the most Perlish way to do it. But since this is Perl, there is more than one way to do it, so here are a few other fun ways you could do this:

using List::MoreUtils::natatime ("n-at-a-time"). This method is of course wildly wasteful of memory, creating a scalar for every character in the string.

use List::MoreUtils qw(natatime);

my $in = "aaaaabbbbbcccccdd";
my $out = '';

my $it = natatime 5, split //, $in;
while(my @chars = $it->()) {
    $out .= $_ for @chars;
    $out .= "\n";
}

using the "replacement" argument of substr to splice in newlines, working from the end: (you have to work from the end because otherwise further offsets no longer line up after you start adding newlines; also working from the end means you only calculate length $in at loop start time without using an extra variable)

for(my $i = length($in) - length($in) % 5; $i; $i -= 5) {
    substr($in, $i, 0, "\n");
}

if you want to keep the input variable as is, you could pre-calculate all the offsets and extract them using substr

foreach (map $_ * 5, 0 .. int(length($in) / 5)) {
    $out .= substr($in, $_, 5) . "\n";
}

probably the most succinct way using substr is to use replacement and concatenate the return value:

$out .= substr($in, 0, 5, '') . "\n" while $in;
nohat