Очень длинные слова — что делать?

    Суть проблемы


    Возможность пользователя ввести оооочень длинное слово без пробелов, и тем самым «взорвать» layout — это старая проблема. Вот так она решается на Хабре.

    Решать её можно несколькими способами.

    Через стили конечного div


    1) style="word-wrap: break-word" — но этот способ IE-only
    2) style="overflow: auto; white-space: nowrap" — работает везде, стандартное решение. Но появляются полосы прокрутки
    3) style="overflow: hidden; white-space: nowrap" — работает везде, стандартное решение. «Теряется» часть слова (невидна).

    Из PHP


    4) разбить слово мягкими переносами через каждые N символов. Где N — максимально допустимая длина слова, которую вы установили.
    5) удалить такое слово

    В текущем проекте я использовал первые четыре подхода. Последний не казался мне необходимым.

    Казнить нельзя помиловать


    По требованию заказчика был добавлен скрипт контекстной рекламы. И тестер сразу нашёл проблему — он вводил слишком длинное слово, и рекламная система (она ведь контекстная) при попытке отправить такое слово для обработки себе на сервер (даже, если слово было разбито мягкими переносами) просто вешала браузер. Точнее, не то чтобы совсем вешала. IE реагировал и через секунд 20 предлагал оборвать работу скрипта. Firefox почему-то не справился и просто вешался :(

    В общем, встала проблема тотального вырезания слишком длинных слов. Конечно, можно было это сделать стандартным способом — разбить строку по пробелам (\s+) а потом замерив длину каждого слова выкинуть слишком длинные и склеить строку обратно. Но это некрасиво :) Хотелось сделать это только регулярками.

    Решение


    Я ожидал что сработает конструкция вида [^\s]{512,}, где 512 — максимальная разрешённая длина «слова». Однако эта конструкция работать отказалась. Конструкция же [^\s]{512} не подходила, поскольку после неё оставались «хвосты» (например, если слово длиной в 600 символов).

    Немного помучившись пришли к выходу, который удовлетворил всем запросам:

    preg_replace('/[^\s]{512}[^\s]+/', '', $string)

    Таким образом успешно вырезались все длинные слова. И при желании можно было легко «усовершенствовать» механизм, не вырезая слова полностью, а оставляя позволенную длину и добавляя многоточие.

    preg_replace('/([^\s]{512})[^\s]+/', '$1...', $string)

    А как Вы решали подобную проблему?

    UPD: в комментариях так-же предлагают использовать overflow:hidden для всех мест, где пользовательские данные отображаются не в поля ввода.

    UPD2: atukai предложил в усовершенствовании не только ставить многоточие, после позволенного количества символов, но и показывать полное слово в Hint-e.
    То есть будет выглядеть где-то так:
        preg_replace('/([^\s]{25})[^\s]+/', '<span title="$0">$1...</span>', $string)


    UPD3: dandelion напомнил, что я совсем забыл о функции wordwrap. Для данной проблемы ее можно использовать так:
        $string = wordwrap($string, 512, '&shy;', true);
    Однако нужно помнить, что эта функция некорректно работает с UTF-8.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 80
    • +12
      Какие-то впечатлительные пользователи Хабра за демонстрацию длинного слова тут-же насрали в карму.
      Огромное "спасибо" :(
      • –4
        А зачем его было демонстрировать - просто чтобы порвать ленту в клочья?
        Если это было так критично - почему не поставить ссылку с обьяснением или сделать это прямо в этом посте? Хотя и в таком случае заминусовали бы...
        • 0
          ссылка уже есть, вижу... просто сначала лично я принял тот пост за спам
          • 0
            Я не сразу понял в чем дело, только после первого минуса... :) Но да, теперь там объяснение и ссылка.
        • +9
          Это веб 2.0 - на хабре проблема длинных слов решается именно так.
          • +3
            А чего вы хотели от этих задротышей?
          • 0
            А по-моему, очень полезная статья. Спасибо. При случае воспользуюсь.
            • 0
              Было интересно решение со стилями.
              Может быть кто-то знает, существует какое-то более универсальное решение с помощью стилей?
              Или легеньгий жабаскрипт подкиньте накрайняк :)
              • 0
                Самое простое для дива с контентом:

                overflow:hidden

                Особо длинные слова обрезаются по ширине дива. Элементарно.
                • 0
                  Трудно назвать подходящим решением, ведь слов просто становится не видно. Что модет нести потерю смысла.
                  • +1
                    довольносложносказать,чтооченьдлинноесловонесетсмыслвкоторыйктотобудетвчитыватьсяиточтоононедостойнообрезания
                    короче, написал длинную белебирду - не обижайся что она обрежется
                    все остальное - будет по границам дива и будет переноситься корректно
                    • 0
                      Вот смотрите. Сейчас наши комменты все время уменьшаются по ширине. Чем дальше будет идти эта ветка, тем меньше места будет оставаться. И довольно скоро вполне безобидное Zend_Controller_Action или Zend_Helpep_Action_invertCharacters уже не будет влезать по ширине.
                      • 0
                        Это говорит только о том, делать древовидные комментарии неограниченной вложенности путем простого добавления отступа слева, за счет уменьшения области контента - неправильно.

                        Откройте например http://habrahabr.ru/blog/apple/38023.htm…
                        и сожмите окно чтобы увидеть этот эффект: по слову в каждой строке.

                        Как решение этой проблемы - можно сделать минимальную ширину блока равной 80-100 символов.
                        А чтобы сохранить отступ достаточно сделать отступ не равномерным, а уменьшающимся с приростом уровня вложенности.
                        • 0
                          Ну, насколько видите, есть случаи (например как у меня - с рекламой) когда overflow:hidden не спасает.
                    • 0
                      Интересно, какое слово такой длины может потерять смысл?
                      Это либо ссылка, либо чья-то шутка, либо у кого-то на клаве пробел не работает и человек это заметил после того, как отправил сообщение. В других случаях это бред писавшего.
                      • 0
                        например японцы практически не используют пробелов.
                        причём если где нибудь не там, автоматически вставить пробел,
                        то смысл фразы может очень сильно поменятся :(
                        • +2
                          Вы часто для японцев кодите?
                          • 0
                            вообще то, да :)
                            • 0
                              А можно посмотреть работы? :)
                              • 0
                                К сожалению нет :(
                                (небольшие веб-приложения, для внутреннего использования заказчега)
                    • 0
                      Хотя, конечно, удаление тоже такое делает, но удаление дает слову шанс, :) а overflow:hidden - нет.
                    • 0
                      Очень интересный пост.
                      И решение красивое.
                      ЗЫ Мог бы плюсануть - плюсанул.
                      • 0
                        повторюсь, но там где данные вводит третье лицо - overflow: hidden
                        • +2
                          1) разбить слово мягкими переносами через каждые N символов. Где N - максимально допустимая длина слова, которую вы установили. Так всегда и делаю.

                          >> Таким образом успешно вырезались все длинные слова. И при желании можно было легко "усовершенствовать" механизм, не вырезая слова полностью, а оставляя позволенную длину и добавляя троеточие.

                          А к тому же при наведении на слово в подсказке показывать все слово. Например, на Группах Гугла так скрываются емайлы
                          • 0
                            вот такой способ использую я

                            function smarty_modifier_splitword($string, $wordmaxlen = 50, $hyp = " ")
                            {
                                    return preg_replace('/([^\s]{' . intval($wordmaxlen) .'})+/U', '$1' . $hyp, $string);
                            }

                            в качестве $hyp можно использовать &shy; — символ мягкого переноса. Правда говорят что этот символ не всегда корректно работает. У меня firefox 3b4 под Mac OS — в нем все работает правильно.
                            • 0
                              В FireFox 2.0 мягкие переносы не работают.
                          • 0
                            может ставить принудительные пробелы? Сымысля думаю не теряе ться
                            • НЛО прилетело и опубликовало эту надпись здесь
                              • +2
                                А как же функция wordwrap() в пхп? Я сам не юзаю, но судя по мануалу - практичное решение.
                                string wordwrap ( string str [, int width [, string break [, boolean cut]]] )
                                <?php
                                $text = "Очень длинное слоооооооооооооооово.";
                                $newtext = wordwrap($text, 8, "\n", 1);
                                echo "$newtext\n";
                                ?>
                                • 0
                                  Спасибо, тоже верно. Хотя у этой функции и есть проблемы с кодировкаи, в отличие от регулярных выражений. Добавлю.
                                  • 0
                                    ну, не совсем практичное 8) нет встроенной поддержки mb_wordwrap, для тех кто идёт в ногу со временем.
                                    • 0
                                      в комментах по функции wordwrap на php.net есть много хороших решений
                                    • 0
                                      Долго думал над "первые три подхода". Сделайте сквозную нумерацию, раз так пишете.
                                      • 0
                                        Согласен, непонятно.
                                      • +1
                                        Разбиение на слова пробелами не поможет в случае, если символами будут знаки препинания, например "!!!! !!!!" - не будет переноситься.

                                        Также проблемы с переносом возникнут в случае "привет ,привет" - тут запятая поставлена не на то место.

                                        К тому же wordwrap имеет смысл толькол в том случае, если у вам текст не сорежит html, если же он содержит, то она не подойдет и придется что-то сове писать... к тому же проблемы с UTF-8...
                                        • 0
                                          Не бояться потерять часть окончания, заменив многоточием.

                                          $length = "100"; # Указываем сколько нужно
                                          @array = split(/\s+/, $_[0]);
                                          $_[1] = @array;
                                          foreach $element(@array) {
                                          $element_end = (length($element) > $length) ? "..." : "";
                                          $element = substr($element, 0, $length).$element_end;
                                          }
                                          $_[0] = join (" ", @array);
                                          • 0
                                            какой это язык? смутно похоже на php, но конструкции типа foreach $element(@array) { вводят меня в ступор. :)
                                          • 0
                                            Да уж, решение не в стиле Perl. Вот примерно так надо:

                                            #!/usr/bin/perl
                                            my $str = qq(One of your old favourite songs from way back when);

                                            my $len = 8; # максимально возможная длина слова, которая нас устраивает

                                            $str =~ s!(\S{$len,})!" ".substr($1, 0, ($len == length($1) ? $len - 3 : $len))."… "!eg;

                                            print $str;
                                            • 0
                                              Доктор, perl -e '$a="longworldverylongworld aaaaaaaaaa vvvvvvvv"; $a=~s/\w{5,}/.../g; print $a'
                                          • +2
                                            Нет слова "троеточие" в русском языке.
                                            Есть "многоточие".
                                            • 0
                                              Я пошел в решении этой проблемы с другого конца - не пытаться придумывать магические способы, как с помощью CSS, JavaScript, PHP вывести длинный контент, уже попавший в базу, а предотвратить его поподание в момент ввода пользователем (администратором), указав ему, что контент вылезает за рамки предусмотренные дизайном.

                                              Примеры использования с описанием и исходниками: http://goldenman.spb.ru/development/jquery/limiter/
                                              • 0
                                                А если на ресурсе позволено публиковать ссылки? Т.о. пользователь не сможет опубликовать длинную сложную ссылку.
                                                • 0
                                                  Если у нас гиперссылка, то программа в теге <A> её не трогает, а зрителю показывает усечённый вариант, к примеру (если использовать снова Perl, смотрим вышеприведённый пример):

                                                  $text_after = "<A HREF=$text_to TARGET=_blank>".substr($text_to, 0, $length)."...</A>";
                                                  • 0
                                                    В этом случае делаем, чтобы рендер воспринмал вводимый текст не как plain-text, а как html. То есть в этом плане никаких проблем - хоть ссылки, хоть черти-чё. В примерах этого нету, так как у меня не возникало такой задачи, но вопрос хороший - нужно будет добавить поддержку ввода HTML-ем.
                                                • 0
                                                  • 0
                                                    В смысле, способ имитировать мягкий перенос в FF
                                                    • 0
                                                      Лучше дождаться релиза FF3 (не бета), и всем радостно перейти на него. :)
                                                      • 0
                                                        Люди и на FF1.5 сидят, так что куда там…
                                                  • 0
                                                    Нашёл отличный вариант расстановки мягких пробелов: тег wbr (http://www.quirksmode.org/oddsandends/wb…). Его и пользуем.
                                                    Для оперы отдельное правило в css: wbr:after { content: "\00200B" }
                                                    • 0
                                                      В «Опере» &shy; работает, там ни к чему костыли.
                                                      • 0
                                                        На фоне фокусов IE, правило для оперы не считаю костылём. После анализа статьи по ссылке пришёл к выводу, что использование тега wbr - наиболее приемлимый вариант. Иначе пришлось бы генерировать разный код для разных браузеров. Как говорится, меньшее из двух зол.
                                                        • 0
                                                          В IE и Opera работает «&shy;». Именно использование WBR и прочего является костылём для FF.
                                                          • 0
                                                            Поскольку мне нужно, чтобы работало везде, пришлось использовать костыль. Кстати интересно, как вы решаете эту проблему? Ну допустим, ­ для ие и оперы. А в других браузерах? Отдельный код?
                                                            • 0
                                                              обычно — overflow, но я придумал более пугающий способ :)

                                                              http://bolknote.ru/2007/11/03/~1444/
                                                              • 0
                                                                На вкус и цвет... :)
                                                                • 0
                                                                  Ну, это просто разные вещи — в моём способе символ переноса появляется. А с WBR — нет.
                                                      • 0
                                                        Вставляем в длинные слова .
                                                        Для оперы css fix:
                                                        wbr:after { content: "\00200B" }
                                                        • 0
                                                          Повторяемся :)
                                                        • +1
                                                          [^\s]{512,}? Что это ещё такое? \S{512,} не пробовали?
                                                          • –1
                                                            Пишем
                                                            style="overflow:hidden; /> и не будет полос прокрутки
                                                            • 0
                                                              недавно заюзал примерно такую конструкцию
                                                              $s=chunk_split($s,40,"\n"...

                                                              а для ссылок конечно правильно быдут показывать часть ссылки с полным title
                                                              • 0
                                                                Я конечно не гуру ПХП, говорят язык красивый.

                                                                Но чем вас не устраивает такая конструкция: $text =~ s/\w{5,}/.../g;?
                                                                • 0
                                                                  Наверное тем, что это перловая конструкция, да? :)
                                                                  • 0
                                                                    ну так и в пхп должен быть substitution, ПХП же прямой наследник Перла.
                                                                    • 0
                                                                      В статейке выше все написано.
                                                                      • 0
                                                                        Улыбнуло, статейка.
                                                                • 0
                                                                  Как я посмотрю, большинство комментаторов склоняется к разбиению длинных слов пробелами. Да это будет работать если это действительно слово, но если это просто будет длинная ссылка, например ведущая на поисковые результаты яндекса, установка пробела сделает эту ссылку не работоспособной. Так что все конструкции устанавливающие пробел необходимо дополнять проверкой не начинается ли это длинное слово с http:// https:// ftp:// и пр.
                                                                  • 0
                                                                    Я использую для разбивки слов $s=chunk_split...
                                                                    А для ссвлок считаю рациональным показывать часть ссылки с полным title
                                                                    • 0
                                                                      А тег уже не работает?)
                                                                      • 0
                                                                        ой) я про тег NOBR говорил)
                                                                      • 0
                                                                        Вот тут написано про text-overflow
                                                                        http://habrahabr.ru/blog/webdev/35441.html
                                                                        • 0
                                                                          Ещё могу такой вариант предложить.
                                                                          • 0
                                                                            Есть подобное в посте по ссылке выше, но там только для Лисы.
                                                                            А почему у вас background-repeat: no-repeat; а не background-repeat: repeat-y; ?
                                                                            • 0
                                                                              Потому что там картинка по высоте равно высоте строки :)
                                                                              Но через repeat-y; безусловно правильней, ну и пнгшку высотой 1px
                                                                          • 0
                                                                            pre, code {
                                                                            white-space: pre-wrap; /* css-3 */
                                                                            white-space: -moz-pre-wrap; /* Mozilla */
                                                                            white-space: -pre-wrap; /* Opera 4-6 */
                                                                            white-space: -o-pre-wrap; /* Opera 7 */
                                                                            word-wrap: break-word; /* Internet Explorer 5.5+ */
                                                                            }
                                                                            • 0
                                                                              word-wrap: break-word теперь работает практически во всех браузерах, неплохо бы поправить статью.
                                                                              • 0
                                                                                В этом варианте как быть с кириллицей? Так кириллица обрезается в два раза короче.

                                                                                preg_replace('/([^\s]{30})[^\s]+/', '$1...', $string)

                                                                                и остается артефакт:

                                                                                считывать/запис�...

                                                                                • 0
                                                                                  скорее всего у вас не выставлена правильная локаль

                                                                                  setlocale('ru_RU.UTF-8')
                                                                                  


                                                                                  должно решить проблему.

                                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.