Разделить строку на фрагменты фиксированной длины и записать в отдельную строку в Raku

У меня есть файл test.txt:

Stringsplittingskills

Я хочу прочитать этот файл и записать в другой файл out.txt с тремя символами в каждой строке, например

Str
ing
spl
itt
ing
ski
lls

Что я сделал

my $string = "test.txt".IO.slurp;
my $start = 0;
my $elements = $string.chars;
# open file in writing mode
my $file_handle = "out.txt".IO.open: :w;
while $start < $elements {
    my $line = $string.substr($start,3);
    if $line.chars == 3 {
        $file_handle.print("$line\n") 
    } elsif $line.chars < 3 {
        $file_handle.print("$line")
    }      
    $start = $start + 3;
}
# close file handle
$file_handle.close

Это нормально работает, если длина строки не кратна 3. Если длина строки кратна 3, в конец выходного файла вставляется дополнительный символ новой строки. Как я могу избежать вставки новой строки в конце, если длина строки кратна 3?

Я попробовал другой, более короткий подход,

my $string = "test.txt".IO.slurp;

my $file_handle = "out.txt".IO.open: :w;
for $string.comb(3) -> $line {
    $file_handle.print("$line\n")
}

Тем не менее, он страдает той же проблемой.

Я искал здесь, здесь, но все еще не может решить эту проблему.


person Suman Khanal    schedule 18.05.2020    source источник


Ответы (3)


Другой подход с использованием substr-rw.

subset PositiveInt of Int where * > 0;

sub break( Str $str is copy, PositiveInt $length )
{
    my $i = $length;

    while $i < $str.chars
    {
        $str.substr-rw( $i, 0 ) = "\n";
        $i += $length + 1;
    }

    $str;
}

say break("12345678", 3);

Выход

123
456
78
person Holli    schedule 21.05.2020

Правильный ответ - конечно, использовать .comb и .join.

Тем не менее, вот как вы можете исправить свой код.


Вы можете изменить строку if, чтобы проверить, находится ли она в конце, и использовать else.

if $start+3 < $elements {
    $file_handle.print("$line\n") 
} else {
    $file_handle.print($line)
}

Лично я бы изменил его так, чтобы только добавление \n было условным.

while $start < $elements {
    my $line = $string.substr($start,3);
    $file_handle.print( $line ~ ( "\n" x ($start+3 < $elements) ));
    $start += 3;
}

Это работает, потому что < возвращает либо True, либо False.

Начиная с True == 1 и False == 0, оператор x повторяет \n не более одного раза.

'abc' x 1;     # 'abc'
'abc' x True;  # 'abc'

'abc' x 0;     # ''
'abc' x False; # ''

Если бы вы были очень осторожны, вы могли бы использовать x+?.
(На самом деле это 3 отдельных оператора.)

'abc' x   3; # 'abcabcabc'
'abc' x+? 3; # 'abc'

infix:« x »( 'abc', prefix:« + »( prefix:« ? »( 3 ) ) );

Я бы, наверное, использовал loop, если бы собирался структурировать его вот так.

loop ( my $start = 0; $start < $elements ; $start += 3 ) {
    my $line = $string.substr($start,3);
    $file_handle.print( $line ~ ( "\n" x ($start+3 < $elements) ));
}

Или вместо того, чтобы добавлять новую строку в конец каждой строки, вы можете добавить ее в начало каждой строки, кроме первой.

while $start < $elements {
    my $line = $string.substr($start,3);

    my $nl = "\n";

    # clear $nl the first time through
    once $nl = "";

    $file_handle.print($nl ~ $line);

    $start = $start + 3;
}
person Brad Gilbert    schedule 19.05.2020
comment
Спасибо за множество подходов. Должен знать once функцию. - person Suman Khanal; 19.05.2020
comment
@SumanKhanal Обратите внимание, что once - один раз на клон закрытия. Как правило, это то, что вы в любом случае хотите. for ^5 { for ^3 { once say 'hi' }} говорит hi 5 раз, так как внутренний цикл клонируется 5 раз. - person Brad Gilbert; 21.05.2020

person    schedule
comment
Работает как шарм. Спасибо. Похоже, я слишком много думал! :) - person Suman Khanal; 18.05.2020