понедельник, 21 ноября 2016 г.

regex word wrapping в духе notepad

Иногда возникает необходимость разбить строку на подстроки, чья длина не превышает заданную максимальную длину. При этом, если слово не умещается в текущей строке, то его необходимо целиком перенести на новую строку.

Т.е. нам надо симулировать поведение функции Word Wrap из приложения Notepad в Windows.

Есть два варианта решения этой задачи:
  1. Написать алгоритм
  2. Прикрутить регулярные выражения. И если учесть, что такая задача может возникнуть где угодно (т.е. как на клиенте, так и на сервере), а регулярные выражения поддерживаются практически всеми языками программирования, то этот вариант кажется наиболее предпочтительным.

Вот просто пример на C#, когда нам нужно разбить строку с длинным почтовым адресом longAddressString и представить результат в виде коллекции строк, чья длинна не превышает 40 символов.

List<string> adresses = (from Match m in Regex.Matches(longAddressString, @"(?:((?>.{1,40}(?:(?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|.{1,40})(?:\r?\n)?|(?:\r?\n|$))") where !String.IsNullOrWhiteSpace(m.Value) select m.Value ).ToList();

А ниже объяснение работы данного регулярного выражения от автора.

# MS-Windows  "Notepad.exe Word Wrap" simulation
 # ( N = 16 )
 # ============================
 # Find:     @"(?:((?>.{1,16}(?:(?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|.{1,16})(?:\r?\n)?|(?:\r?\n|$))"
 # Replace:  @"$1\r\n"
 # Flags:    Global     

 # Note - Through trial and error discovery, it apparears Notepad accepts an extra whitespace
 # (possibly in the N+1 position) to help alignment. This matters not because thier viewport hides it.
 # There is no trimming of any whitespace, so the wrapped buffer could be reconstituted by inserting/detecting a
 # wrap point code which is different than a linebreak.
 # This regex works on un-wrapped source, but could probably be adjusted to produce/work on wrapped buffer text.
 # To reconstitute the source all that is needed is to remove the wrap code which is probably just an extra "\r".

 (?:
      # -- Words/Characters 
      (                       # (1 start)
           (?>                     # Atomic Group - Match words with valid breaks
                .{1,16}                 #  1-N characters
                                        #  Followed by one of 4 prioritized, non-linebreak whitespace
                (?:                     #  break types:
                     (?<= [^\S\r\n] )        # 1. - Behind a non-linebreak whitespace
                     [^\S\r\n]?              #      ( optionally accept an extra non-linebreak whitespace )
                  |  (?= \r? \n )            # 2. - Ahead a linebreak
                  |  $                       # 3. - EOS
                  |  [^\S\r\n]               # 4. - Accept an extra non-linebreak whitespace
                )
           )                       # End atomic group
        |  
           .{1,16}                 # No valid word breaks, just break on the N'th character
      )                       # (1 end)
      (?: \r? \n )?           # Optional linebreak after Words/Characters
   |  
      # -- Or, Linebreak
      (?: \r? \n | $ )        # Stand alone linebreak or at EOS
 )

Комментариев нет:

Отправить комментарий