Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Регулярные выражения > Как обрезать строку до не разбивая слов


Автор: sir_nuf_nuf 5.10.2009, 14:06
Товарищи, хочу предложить вам жизненную задачу на регулярки:

Задача проста:
Нужно обрезать строку до заданной длинны, стараясь по возможности не разбивать слова.
В конце строки нужно дописать троеточие (типа "есть продолжение")

Требование:
1) результат, вместе троеточием должен быть не длиннее указанной числа символов 
2) не разбивать слова, т.е. обрезать предложение нужно между словю
3) между последним словом и троеточием не должно быть пробелов.


Я пока решил эту задачу на if - решил не извращаться с регуляркой.

Кто сможет написать regexp ?

Автор: gcc 5.10.2009, 14:38
а что должно получиться?

вот например я делал обрезать длинные слова и длинные строки...

Код


sub stat_cut_big : Private {
    my ( $self, $c, $text_cut ) = @_;
    $text_cut ||= '';

    return length($text_cut) > 30 ? substr( $text_cut, 0, 30 ) : $text_cut;

}

sub stat_cut : Private {
    my ( $self, $c, $text_cut ) = @_;
    my $cut_hash;
    $text_cut ||= '';
        my $h;
    my @len = split( / /, $text_cut, 8 );
    my $big;
    foreach (@len) {
        if ( length($_) > 16 ) {
            $big = 1;
            # binmode(STDOUT,":utf8");
             $_ = Encode::decode('utf8', $_);

#use MIME::Base64;
# use Encode qw(_utf8_off);
#use utf8;
# $str = 'Текст в utf-8';
#_utf8_off($_);
#$_ = encode_base64($_, '');            
            
            $_ = substr $_, 0, 16;
            $_ .= '.. ';
          $_ = Encode::encode('utf8', $_ );

        }
    }

    return $big ? join( ' ', @len ) : $text_cut;

}


так вот вызвать:
Код

    $c->stash->{new_section_id1} = $loop_data->[0]->[0];
    $c->stash->{new_section_all1} =
      $c->forward( 'stat_cut_big', [ $loop_data->[0]->[1] ] );
    $c->stash->{new_section_cut1} =
      $c->forward( 'stat_cut', [ $loop_data->[0]->[1] ] );

Автор: shamber 5.10.2009, 15:21
gcc, это регулярка?

Автор: gcc 5.10.2009, 15:35
shamber, точно нет... 

Автор: sir_nuf_nuf 5.10.2009, 16:01
должно получиться примерно следующее:
Код

print wordcate("big bang theory",  13);
#bing bang...


Автор: KSURi 5.10.2009, 19:21
Код

#!/usr/bin/evn perl

use Modern::Perl;

my @data = (
    'Hello World!',
    'This is very-very-very-very-very long string. Really long.',
    'This one is short.',
);
s{^.{0,13}\K\s.*$}{...} for @data;
say for @data


Легко можно сделать жадный вариант (нарушает п. 1, но на всякий случай):
Код

s{^.{13,}\K\s.*$}{...} for @data;

Автор: sir_nuf_nuf 5.10.2009, 23:18
KSURi, не работает.
видно на 'This is very-very-very-very-very long string. Really long.'

Автор: amg 6.10.2009, 07:03
Вот что я придумал (правда, не только регулярка, плюс к ней еще pos и пробелы в начале строки нужно удалять)
Код
my @data = ('
Hello!','
Hello World!','
This is very-very-very-very-very long string. Really long.','
This one is short.','
Very-very-very-very-very_long_word',
);

foreach (@data) {
  s/^\s+//;
  pos = 13-3;
  s/(?:\s+\S*|)\G.{4,}/.../;
  print length,": '$_'\n";
}


Автор: gcc 6.10.2009, 07:29
а почему нельзя написать без регулярки? регулярка быстрее будет?

просто пока это все понапридумывать - так очень много времени тратиться на это

Автор: KSURi 6.10.2009, 08:07
Цитата(sir_nuf_nuf @  5.10.2009,  23:18 Найти цитируемый пост)
KSURi, не работает.
видно на 'This is very-very-very-very-very long string. Really long.' 

Почему? У меня выводит "This is...".

UPD:
Нашел пару недочетов, поправил.  Вроде не нарушает ниодного пункта.
Код

my $total_max = 13;
my $ending = '...';
my $meaning_max = $total_max - length $ending;
length > $total_max and s/^.{0,$meaning_max}\K[\s-].*$/$ending/ for @data;

Автор: arto 6.10.2009, 14:42
perl -le 'print $ARGV[0] =~ m#^(.{15,24}\S)\b# ? "$1..." : $ARGV[0]'

Автор: Logo 8.10.2009, 09:15
А что считать словом? Машинное слово? Тогда:
Код

$_ = 'This is very-very-very-very-very long string. Really long.';
$maxn = 15;
$n = $maxn - 3;
m/^.{0,$n}\b(?=(?:\W|$))/;
print "$&...";


Цитата

а почему нельзя написать без регулярки? регулярка быстрее будет?

просто пока это все понапридумывать - так очень много времени тратиться на это 


Что бы развить моск. При тренировке регулярками как правило наоборот, время меньше тратится.

Автор: amg 8.10.2009, 10:43
Logo, а если
$_ = 'This_is_very_very_very_very_very_long_word.'; smile

Автор: Logo 8.10.2009, 13:10
amg, 
Можно добавить
Код

m/^.{0,$n}(?:\b(?=(?:\W|$))|^)/;


Автор: amg 8.10.2009, 15:37
Цитата(Logo @  8.10.2009,  13:10 Найти цитируемый пост)
Можно добавить...
 Не помогает. Все равно длинное слово вырезается полностью. (хотя в условии, строго говоря, не сказано, что такого быть не должно, но исходя из здравого смысла, логично было бы оставлять часть слова).

Кроме того, пусть, например, строка содержит ровно $maxn символов. Твоё решение такую строку обрежет. А логично было бы оставить ее как есть -- она же помещается.

И к совсем коротким словам добавит ... Еще одну проверку? (В условии то сказано "только регуляркой". Хотя этого никто пока не смог соблюсти).

Автор: Logo 8.10.2009, 16:11
Цитата

Не помогает. Все равно длинное слово вырезается полностью. (хотя в условии, строго говоря, не сказано, что такого быть не должно, но исходя из здравого смысла, логично было бы оставлять часть слова).

Ну да, я и поправил только некорректное не обявленное значение переменной.

Цитата

Кроме того, пусть, например, строка содержит ровно $maxn символов. Твоё решение такую строку обрежет. А логично было бы оставить ее как есть -- она же помещается.

Верно, и если она меньше $maxn... Сама регуляка только делает "результат, вместе троеточием должен быть не длиннее указанной числа символов". Можно поставить if на длинну строки в коде, хотя и регуляркой тоже можно
Код

$maxn = 8;
$n = $maxn - 3;
print "$&..." if m/^(?=.{$maxn}.+$).{0,$n}\b(?=(?:\W|$))/s;


Автор: Logo 10.10.2009, 13:50
Одной регуляркой, и с учетом пожеланий amg. Хотя это уже изврат, наверное smile. Разобьет, если на границе слово длинной  7 и более символов. Словом считается [a-z]([a-zа-яё'-]*[a-zа-яё])? ,  т.е. don't и Римский-Корсаков проходят.
Код

$_ ='This is very-very-very-very-very long string. Really long.';
print strip($_, 20);

sub strip {
  my $maxn = pop;
  my $n = $maxn-3;
  return $_[0] =~ m{
  ^.*?
  (?=.{$maxn}.)
  (?:
      ((?=\s* ((?<=.{$n}))? [a-zа-яё] (?:[a-zа-яё'-]){5,} [a-zа-яё] ((?<=..{$n}))) (?(3)(?(2)(?!)|)|(?!)) )|
      (?=\s*[a-zа-яё](?:[a-zа-яё'-]*[a-zа-яё])?(?<=..{$n}))|
      (?<=.{$n})
  )
  (?(1)\s*.*?(?<=.{$n}))
  }xs ? "${^MATCH}..." : $_[0];
}




Цитата

Не помогает. Все равно длинное слово вырезается полностью. (хотя в условии, строго говоря, не сказано, что такого быть не должно, но исходя из здравого смысла, логично было бы оставлять часть слова).

Хотя на мой взгляд, если разбивать, то не по длине слова, а по длине части слова, оказавшейся целой.

Цитата

И к совсем коротким словам добавит ... Еще одну проверку? (В условии то сказано "только регуляркой". Хотя этого никто пока не смог соблюсти).

Про это непонял

Автор: ad280379 2.12.2009, 08:27
Цитата(arto @ 6.10.2009,  14:42)
perl -le 'print $ARGV[0] =~ m#^(.{15,24}\S)\b# ? "$1..." : $ARGV[0]'

помоему это есть наиболее правильное решение на основе регулярных выражений.
единственно не понимаю (т.е. считаю что не правильно)  указание \S (вместо \s) и не понятно зачем нижняя граница {15,24}

я переписал это так (в данном решении N количество выводимых символов)
формат - prg_name.pl N "Hello and welcome dears"
результат при N=10 будет "Hello and ..."

---code---
$text = $ARGV[1];
$ltext = length($ARGV[1]);
print "[$ltext],[$ARGV[0]],$text" if($text=~m/^(.{0,$ARGV[0]}\s*)\b/ ? ($text="$1...") : ($text.="..."));
---code end---


конструктивная критика приветствуется  smile 

Автор: Logo 12.12.2009, 22:09
Цитата

помоему это есть наиболее правильное решение на основе регулярных выражений.

Очень коротко и аргументировано smile 

Автор: ad280379 12.12.2009, 23:52
Цитата(Logo @ 12.12.2009,  22:09)
Цитата

помоему это есть наиболее правильное решение на основе регулярных выражений.

Очень коротко и аргументировано smile

ну... вы же Бывалый... вообще должны с двух слов понимать

Автор: amg 14.12.2009, 12:06
Господа!
По моему, мы несколько неправильно понимаем задачу. Насколько я себе представляю, она состоит не в том, чтобы написать код, делающий сформулированные в 1-м посте вещи, и даже не в том, чтобы этот код был самым коротким/изящным, а в том, чтобы сделать это только ругуляркой. И такая постановка задачи, IMHO, так пока и не решенной, оправдана. Во-первых, тогда решение будет почти прямо переносимо на любой ЯП, где есть PCRE, во-вторых, существуют программы a'la grep, которым на вход нужно давать регулярное выражение и ничего кроме него.

Автор: Logo 14.12.2009, 23:09
Цитата

И такая постановка задачи, IMHO, так пока и не решенной

А как же мой код smile 

Автор: amg 16.12.2009, 11:16
Цитата(Logo @  14.12.2009,  23:09 Найти цитируемый пост)
А как же мой код
И в самом деле. А я твой код как-то пропустил, т.к. после беглого взгляда на него он у меня не ассоциировался с регулярным выражением (они обычно не так выглядят) smile .

Автор: ad280379 16.12.2009, 20:11
Цитата(Logo @ 14.12.2009,  23:09)
Цитата

И такая постановка задачи, IMHO, так пока и не решенной

А как же мой код smile

А как же мой код  smile я старался  smile 
 smile  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)