Не так давно передо мной встала задача иметь возможность хранить данные в текстовом виде, чтобы с ними работала не только программа, но мог прочитать и отредактировать (а также создать с нуля в текстовом редакторе) человек. Для этого уже существует множество удобных и хороших форматов, например JSON, YAML, XML и так далее. Но в рассмотренных системах попадались моменты, которые, все же, немного не понравились.

Уделю особенное внимание яркому неудобству большинства таких форматов (естественно, на мой взгляд), в том числе и очень мощных и популярных, — проблеме, связанной с хранением текста: как записать текстовое поле, которое может содержать любые текстовые символы, чтобы его содержимое не приходилось менять, и оно не повлияло на парсинг, ведь там могут встретиться и различные подстроки, совпадающие со служебными комбинациями, и различные нестандартные отступы. Например, в XML текст не должен содержать знаков "<" и ">" — они должны быть заменены на "&lt;" и "&gt;" соответственно. Во многих других системах текст требуется заключать в кавычки. Но что делать, если текст уже содержит кавычки? Использовать другие типы кавычек? Экранировать? Все это означает, что надо вносить изменения в текст, и далеко не факт, что после этого будет удобно читать и редактировать его, если предстоит работа с данными в обычном текстовом редакторе, например, блокноте или полем ввода (textarea) в браузере. Еще есть формат YAML, в котором текст в кавычки не требуется заключать, но там очень важно соблюдать правильные отступы, что для хранения многострочных и многоуровневых данных кажется не очень удобным. Также это увеличивает долю символов, не относящихся к данным — несколько служебных пробелов слева на каждой строке существенно увеличивают вес.

Помимо текста, мне нужно было хранить, по сути, еще два базовых типа данных — целое и дробное число, а также объединения (структуры данных (блоки) и массивы). То есть получается 5 типов: число целое, число с плавающей точкой, текст, структура, массив. Не было необходимости в использовании макросов, выражений и прочих расширений — нужны были просто распределенные по различным блокам и массивам числа и тексты. В связи с такой простотой нужен был максимально тривиальный формат, который мог хранить, ко всему прочему, текстовые поля, учитывая моменты, которые оговаривались выше. Также хотелось видеть данные с как можно меньшим количеством управляющих символов, чтобы было проще понять и запомнить синтаксис.

В общем, был создан велосипед с необычной конструкцией руля формат HV (изначальное внутреннее название — «human values»). На нем я покажу практическое решение указанной проблемы, как это решение вижу я. Формат получился незамудренным — что, в принципе, и требовалось — как уже говорилось, поддерживает всего три простых типа данных (целое число, число с плавающей точкой и текст) и два составных типа (структура данных и массив, которые содержат в себе как простые типы, так и составные). Основных управляющих символов всего 3. Есть еще 3 дополнительных управляющих символа, но это для особых случаев форматирования текстовых полей, а также для обозначения комментариев. Эти случаи относятся к поставленному в статье вопросу (об удобном хранении текстовых полей) и будут рассмотрены ниже на примерах.

Поля данных могут быть однострочными (целое число, число с плавающей точкой, однострочный текст) и многострочными (структура, массив, многострочный текст). Сначала пишется имя поля, затем управляющий символ, который указывает, что значение поля занимает либо одну строку, либо несколько. А затем — значение поля. Если несколько строк, то в конце значения указывается завершающая строка. Собственно, это и есть основная суть формата, изложенная в кратком виде.

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

a: 1
b: 2.2
c: abcd


Здесь представлены 3 простых типа данных:
a — целое число, равное 1
b — число с плавающей точкой, равное 2.2
c — текстовое поле, состоящее из одной строки, равной «abcd»

В следующем примере структура данных и массив, который находится в этой структуре:

xxq+
  a: 12.33
  b: -15
  x+
    : ab
    : cd
    : ef
  ^
^


Здесь структура содержит два поля:
a — число с плавающей точкой, равное 12.33
b — целое число, равное -15
x — массив текстовых полей, которые равны «ab», «cd» и «ef»
Для элементов массива имя поля не пишется.

Сразу скажу, что отступы никакого значения не имеют, и данные в следующем примере абсолютно идентичны данным из предыдущего:

xxq+
a:    12.33
b:       -15
x+
: ab
:      cd
:ef
            ^
^


И вариант представление тех же данных, но вообще без пробелов:

xxq+
a:12.33
b:-15
x+
:ab
:cd
:ef
^
^


Итак, самые главные управляющие символы — ":" (если значение занимает одну строку) и "+" (если значение занимает несколько строк).

А теперь, непосредственно, мое видение решения вопроса представления многострочного текста, содержащего разнообразные символы:

t+
  ABCD
  EFGH<12>@@
  ijklmnopq
  "ABC" + "DEF" = "ABCDEF"
  "A('a')" =//= "B"('''')  abcd
^


В данном примере текст получается таким:

ABCD
EFGH<12>@@
ijklmnopq
"ABC" + "DEF" = "ABCDEF"
"A('a')" =//= "B"('''')abcd


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

Ограничивается текст завершающей строкой. Завершающая строка по умолчанию равна управляющему символу "^". Эта же строка используется для завершения всех многострочных полей, таких как структуры и массивы (показано на примерах выше). Значение будет считываться построчно без учета отступов, пока не встретится завершающая строка. Именно не подстрока, а строка целиком (отступы, как я уже говорил, игнорируются и могут быть любыми).

При записи текстовых полей может возникнуть два вполне резонных вопроса:

1) Что если в исходном тексте встретится строка, которая будет равна завершающей, то есть "^"?
2) Что если отступы в тексте важны и их нельзя игнорировать?

Для разрешения первого случая формат HV позволяет переопределять завершающую строку. Ее просто нужно указать перед значением поля, ну и, соответственно, после:

eee+ END
  hello
  ^
  ^
  ^
  ^
  abcd
END


Текст, содержащийся в поле «eee», такой:

hello
^
^
^
^
abcd


Важный ньюанс — переопределение завершающих строк возможно только для текстовых полей. Остальных многострочные значения (структуры и массивы) всегда завершаются служебным символом "^".

Для разрешения второго случая (отступы имеют значение) HV имеет целых 2 варианта.
Вариант А. Учитывать все отступы справа и слева от текста в каждой строке:

text@
  Это красная строка.
А это обычная строка.
Все отступы от начала строки будут сохранены
   в тексте.
                      Вот так.
^


Вариант Б. Начинать учитывать отступы от первого непробельного символа в каждой строке, причем сам этот первый символ учитываться не будет:

text%
  -А
   *Б
    =В гдеёжзи
^


Текст получится следующим:

А
Б
В гдеёжзи


Интересная особенность - инкапсуляция сериализованных данных в виде текста
Хочу обратить внимание на еще одну особенность, которая хоть и довольно интересная, полезная и почти уникальная, но необходимость в ее применении достаточно редко встречается. Эта особенность автоматически становится доступна благодаря возможности замены завершающей строки, тем самым оставляя оригинальный текст без изменений. Смысл в том, что так можно вставлять одни данные в формате HV в качестве текстового поля в другие данные формата HV. Это не приведет ни к каким к синтаксическим ошибкам при парсинге. Пригодится это может том случае, если имеется несколько обработчиков текстов, находящихся на разных уровнях, и они не знают, в каком формате каждый из них работает — они просто передают текст на следующий уровень.
Например для первого уровня надо передать два массива в формате HV:

a+
  : 1
  : 2
^
b+
  : 3
  : 4
^


Но это надо передать в виде текста через второй уровень:

level_2+
  for_level_1+ &
    a+
      : 1
      : 2
    ^
    b+
      : 3
      : 4
    ^    
  &
^


Поле «for_level_1» является текстовым. Здесь просто заменяется завершающая строка на "&".
Распарсить данные, предназначенные для первого уровня, сразу на втором уровне нельзя по условиям примера — второй уровень не знает, как должен обрабатываться этот текст — может там HV, может JSON, а может просто текст, не предназначенный для парсинга. Это решает первый уровень (по условиям примера).

То есть, в текстовом поле HV можно передать любые сериализованные данные — хоть тот же HV, хоть JSON, XML, YAML и так далее. Возможности безопасной инкапсуляции без редактирования текста я не встретил ни в одном из рассмотренных форматов. Эта фича хоть и редко где может понадобиться, но все-таки.


Итак, основных ключевых символов получилось 3 штуки:

: — значение в одной строке
+ — значение в нескольких строках
^ — конец многострочного значения

И 3 дополнительных:

@ — форматированный многострочный текст
% — размеченный многострочный текст
# — комментарий

Нет обязательных скобок, кавычек, явных указаний типов данных. Типизация и все проверки на соответствие осуществляются в обработчике HV — ему известно заранее, какие имена полей могут встретится и значения какого типа и формата они должны содержать. Чрезмерная простота делает его портируемым практически на любой язык программирования.

При первом рассмотрении HV может показаться похожим на YAML — тоже минималистичный, тоже текст без кавычек. Но, поскольку HV создавался с нуля, а не на основе какого-либо существующего формата, то различий с YAML больше, чем сходств. HV нетребователен к отступам. Общая доля служебного текста в HV формате меньше, потому что YAML требует соблюдение отступов и часто использует комбинации, состоящие из 2-х и более символов, например "---", ": |-", ": >", а HV — всегда только одиночные символы. Ну а механизма, который ограничивает текст переопределеяемой завершающей строкой, — я не встретил ни в одном из рассмотренных форматов. А как мне кажется, это достаточно удобный и наглядный механизм.

В общем, получился такой лаконичный формат для хранения простых данных для удобного, на мой взгляд, восприятия человеком. Конечно, здесь нет хранения функций, ассоциативных массивов, макросов, препроцессоров, замыканий, арифметических выражений и прочих крутых штук, которыми могут похвастаться многие другие форматы. Но эти навороты и не требуются, поскольку формат HV выполняет и решает поставленные перед ним задачи, которые были оговорены выше, например, не требует заключать текст в кавычки или скобки, не требует экранирования символов, не требует явно указывать тип данных, выглядит довольно тривиально, поддерживает самый базовый набор типов, использует мало служебных символов и т.д.

Надеюсь, я правильно смог изложить причины создания формата HV и его особенности. Если что-то все-таки недообъяснил — буду рад ответить на адекватные вопросы.

Для тех, кто хочет ознакомиться получше с форматом HV, на ресурсе http://vaomark.com/z23F0Cz размещено более подробное описание и куча примеров, охватывающих все стороны.

Там же можно скачать актуальный исходный код обработчика HV и модуль тестирования на Python 2.7. Кстати, в скором времени планируется портировать обработчик на C++, Java, PHP и другие языки — все будет доступно все по той же ссылке.

P.S.: Формат HV построен на моем видении решения задачи хранения текстовых полей в сериализованном виде, чтобы значения там находились в оригинальном, неизмененном виде и их можно было удобно читать и изменять в любом простом редакторе. Кто-то посчитает, что получилось удачное решение, кто-то — наоборот; может быть кто-то предложит свое. Кто-то считает, что проблема, затронутая в статье, не такая уж и проблема, что все и так удобно. Хотелось бы узнать Ваше мнение.

Комментарии (33)


  1. lair
    23.11.2015 17:51
    +2

    Например, в XML текст не должен содержать знаков "<" и ">" — они должны быть заменены на "<" и ">" соответственно.

    Вы про CDATA не слышали?

    level_2+
      for_level_1+ &
        a+
          : 1
          : 2
        ^
        b+
          : 3
          : 4
        ^    
      &
    ^
    


    Почему парсер не прекращает чтение level2 на первом ^, а дочитывает до конца?

    А вообще, для задачи «вставить любые данные без редактирования» давно есть известное решение, и оно называется «length-prefixed». Любое другое (известное мне) имеет ограничения.


    1. nikabc555
      23.11.2015 20:14

      Вы про CDATA не слышали?

      CDATA — хорошее решение, но все же, если текст будет содержать в себе закрывающую комбинацию, то придется сделать, как в этом примере из википедии:
      <![CDATA[]]]]><![CDATA[>]]>
      

      то есть, придется как-то менять (в данном случае — дробить) исходный текст.

      А идея использования завершающих строк как раз в том, чтобы можно было подобрать такую завершающую строку, которая в тексте не встретится, например «abc end zzz ///». А если встретится, то назначить другую для этого поля.


      1. lair
        23.11.2015 20:58

        CDATA — хорошее решение, но все же, если текст будет содержать в себе закрывающую комбинацию

        Какова вероятность этого для обычного человеко-читаемого текста?

        А идея использования завершающих строк как раз в том, чтобы можно было подобрать такую завершающую строку, которая в тексте не встретится, например «abc end zzz ///». А если встретится, то назначить другую для этого поля.

        И как вы предлагаете это проверять?

        В структуре (блоке данных) «level_2» указано только одно поле — «for_level_1», [...] Все, что находится между «for_level_1+ &» и "&"

        Как парсер понимает, что внутри level_2 — поле, но внутри for_level_1 — текст?

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

        А человеку обычно и не надо руками запихивать в поле человекочитаемого документа существенный кусок документа на другом (или том же) языке. У вас явно смешаны две разных задачи.


        1. vintage
          23.11.2015 21:12

          100%, если это текст комментария с обсуждением CDATA :-)


          1. lair
            23.11.2015 21:13

            Посмотрите на свой (или мой) комментарий, обсуждающий CDATA, и убедитесь, что это не так.


            1. vintage
              23.11.2015 23:15
              -5

              [Да]
              [Сарказм] [Нет]
              [Что?]


        1. nikabc555
          23.11.2015 21:15

          Отвечу сразу на два вопроса

          Какова вероятность этого для обычного человеко-читаемого текста?

          и
          А человеку обычно и не надо руками запихивать в поле человекочитаемого документа существенный кусок документа на другом (или том же) языке


          Первое, что пришло в голову — человек хочет написать пример использования формата FORMAT и поместить этот пример в текстовое поле в том же самом формате FORMAT. Он использует обычный текстовый редактор, чтобы редактировать все это.

          Для формата HV (который описывается в этой статье) — пример уже есть — как раз «for_level_1». Здесь все решается переопределением завершающей строки. Исходный текст не меняется.

          Для подавляющего большинства известных форматов, например, того же XML.CDATA — придется вносить изменения в исходный текст.


          1. lair
            23.11.2015 21:20

            Первое, что пришло в голову — человек хочет написать пример использования формата FORMAT и поместить этот пример в текстовое поле в том же самом формате FORMAT. Он использует обычный текстовый редактор, чтобы редактировать все это.

            Картинку про буханку знаете? Так вот, зачем?

            Ну и да, как для достаточно большого документа решить задачу гарантированного нахождения символа, не используемого в этом документе, используя только «обычный текстовый редактор»?

            Задачи бывают разные. И такие тоже встречаются.

            Для разных задач удобнее разные инструменты.

            Я предложил вариант решения подобных задач с полным сохранением исходного текста.

            Для этого нужно решить задачу подбора символа, не встречающегося в исходном тексте. Как именно вы планируете это делать?


            1. nikabc555
              23.11.2015 21:31

              Для этого нужно решить задачу подбора символа, не встречающегося в исходном тексте. Как именно вы планируете это делать?


              Не символа, а целой строки (не подстроки!), которая не встречается в исходном тексте. То есть простые вхождения завершающих строк в качестве подстрок в более длинные строки не считаются концом текста.

              1) В тексте несложно найти строки, которые целиком равны завершающей.

              2) Можно переопределять завершающие строки. В особо сложных случаях можно ввести случайный набор символов.


              1. lair
                23.11.2015 21:33

                В тексте несложно найти строки, которые целиком равны завершающей.

                Нашли, что дальше?


        1. nikabc555
          23.11.2015 21:18

          У вас явно смешаны две разных задачи.

          Задачи бывают разные. И такие тоже встречаются.
          Я предложил вариант решения подобных задач с полным сохранением исходного текста.


        1. nikabc555
          23.11.2015 21:22

          Как парсер понимает, что внутри level_2 — поле, но внутри for_level_1 — текст?


          Ответ есть в статье:
          Типизация и все проверки на соответствие осуществляются в обработчике HV — ему известно заранее, какие имена полей могут встретится и значения какого типа и формата они должны содержать.


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


          1. lair
            23.11.2015 21:23

            Эээ, то есть у вас еще есть и предопределенная схема? А в каком формате она задается? А как пользователю, работаюшему с файлом, быть уверенным, что документ соответствует схеме?


        1. popov654
          24.11.2015 11:52

          Как парсер понимает, что внутри level_2 — поле, но внутри for_level_1 — текст?

          Отвечу за автора — может быть, переопределение завершающего символа допускается только для текста, но не для структуры?


          1. nikabc555
            24.11.2015 13:37

            переопределение завершающего символа допускается только для текста, но не для структуры


            Да.
            Я этому ньюансу уделил мало внимания в статье. После Вашего комментария добавил упоминание об этом в статью.


            1. lair
              24.11.2015 14:14

              Зачем для структуры вообще завершающий символ?


              1. nikabc555
                24.11.2015 15:57

                Чтобы парсер понял, что структура закончилась и считана полностью.

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

                Любое многстрочное поле (которым является структура в данном синтаксисе) начинается с имени, а заканчивается завершающей строкой.


                1. lair
                  24.11.2015 16:00

                  Чтобы парсер понял, что структура закончилась и считана полностью. Иначе следующее поле будет считаться частью этой структуры.

                  А как же схема?


    1. nikabc555
      23.11.2015 20:15

      Почему парсер не прекращает чтение level2 на первом ^, а дочитывает до конца?

      В структуре (блоке данных) «level_2» указано только одно поле — «for_level_1», для которого переопределена завершающая строка. Теперь для поля «for_level_1» завершающая строка равна "&"

      Парсер считывает поля и строки последовательно, поэтому он начинает искать строку "&", чтобы завершить чтение поля «for_level_1»

      Все, что находится между «for_level_1+ &» и "&", считывается в качестве текста, поэтому символы "^", которые внутри этого могут встретиться, никак не влияют на парсер.

      После этого он ищет либо новое поле в структуре «level_2», либо завершающую строку для «level_2», которая для равна "^".

      В итоге все парсится без ошибок


    1. nikabc555
      23.11.2015 20:16

      и оно называется «length-prefixed»

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

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


  1. vintage
    23.11.2015 17:55

    Слишком сложно.

    1. Много вариантов записи многострочного текста и каждый со своими ограничениями.
    2. Нужно внимательно следить, чтобы в многострочном тексте не было завершающей конструкции.
    3. Возможность записи без отступов — скорее вредная (человеку сложно воспринимать), чем полезная (экономия на спичках).

    Как насчёт такого варианта? http://habrahabr.ru/post/248147/

    1. Управляющих символов всего 2.
    2. Однозначно указывается какие отступы попадают в значение, а какие нет.
    3. Есть только одна форма записи и её невозможно превратить в нечитаемую лапшу.


    1. nikabc555
      23.11.2015 21:02

      Много вариантов записи многострочного текста и каждый со своими ограничениями

      Я бы назвал не ограничениями, а особенностями.

      Основным символом для обозначения текста все же является "+", т.е. где при парсинге отступы не учитываются. А из двух оставшихся вариантов, я думаю, каждый просто сделал бы для себя выбор, какой из них использовать — либо с отступами от начала строки, либо с отступами от первого символа.

      Формат HV задумывался, как предоставляющий некоторый выбор — это касается как учета отступов в тексте, так и определения завершающих строк. Например, если кому-то нагляднее видеть "!!!THIS IS END!!!" в конце текстового поля — и он смог бы указать использовать именно эту комбинацию.


    1. nikabc555
      23.11.2015 21:03

      Нужно внимательно следить, чтобы в многострочном тексте не было завершающей конструкции.

      Символ "^" выбран не случайно — мало в каком тексте он встретится в качестве одиночной строки. Но если есть опасения, что в тексте он встретится, то можно переопределить завершающую строку.

      На крайний случай, ничто не мешает для отдельно взятого поля определить в качестве завершающей строки, например, "//////////" или даже "!@#$%^^!%@%#&#)($&*#^&*#(#&$^$*$&#^#*$*$&$" — с каждым новым символом шанс совпадения в каком-либо тексте стремится к нулю


    1. nikabc555
      23.11.2015 21:04

      Возможность записи без отступов — скорее вредная (человеку сложно воспринимать), чем полезная (экономия на спичках)

      Это тоже на выбор пользователя. Рассчет был на гибкость. Я считаю, что у каждого свои представления о наглядности, и кому-то может пригодится такая свобода в отступах для улучшения этой самой наглядности.

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

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


      1. vintage
        23.11.2015 22:14

        Зачем вы мне в очередной раз рассказываете как работают ваши костыли, после того как я показал как можно обойтись без костылей вообще?

        Почему вы даёте пользователю выбор как завершить текстовое поле, но не даёте выбор как завершать конец строки? И почему вы не даёте мне выбор называть чёрное белым, а белое чёрным? Я требую, чтобы вы добавили эту несомненно необходимую гибкость в формат! :-) А если серьёзно, то я не хочу каждый раз размышлять о том, чем закончить текстовое поле.

        Не представляю, как удаление отступов может уменьшить читабельность. Вы сейчас высасываете из пальца аргументы для защиты своего детища, вместо того, чтобы улучшить его, своровав идеи у конкурента или же отказаться от велосипеда, если конкурент лучше решает проблемы, поставленные в начале вашей статьи. Вы способны объективно сравнить два решения указанных проблем?


  1. michael_vostrikov
    24.11.2015 10:51

    Как мне кажется, в целом выглядит получше, чем форматы, описанные в других аналогичных статьях на хабре. Возможно, кому-то и пригодится.
    Навскидку вопросы такие:
    Если надо сохранить в поле весь текст этой статьи с комментариями, какой разделитель выбрать? Получается, нужен какой-то редактор, который будет назначать разделители или хотя бы просто проверять валидность разметки.
    Если надо в одном поле сохранить завершающий перевод строки, а в другом нет, какая у них должна быть разметка?


    1. nikabc555
      24.11.2015 14:01

      Если надо сохранить в поле весь текст этой статьи с комментариями, какой разделитель выбрать? Получается, нужен какой-то редактор, который будет назначать разделители или хотя бы просто проверять валидность разметки.


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

      Из внешних инструментов может подойти любой внешний генератор уникальных последовательностей, например UUID или vaomark.com

      Да, способ в этом плане для большого и разнообразного текста не дает 100% гарантии, что без использования методов поиска и/или генерации можно будет подобрать подходящую завершающую строку. Согласен, что это усложняет задачу.

      Но зато исходный текст останется без каких-либо правок. И на мой взгляд, в данном случае, это удобнее, чем если бы пришлось отсчитывать строгое количество отступов (как, например, в YAML) или подменять какие-либо символы.


    1. nikabc555
      24.11.2015 14:21

      Если надо в одном поле сохранить завершающий перевод строки, а в другом нет, какая у них должна быть разметка?


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

      Например:

      text%
        .string 1
        .string 2
        .
      ^
      


      В следующей версии обязательно рассмотрю возможность более удобно это реализовать. Кончено же, с обратной совместимостью.


  1. popov654
    24.11.2015 11:55

    В целом выглядит интересно, правда не очень понял, в чём смысл этого:

    Вариант Б. Начинать учитывать отступы от первого непробельного символа в каждой строке, причем сам этот первый символ учитываться не будет:

    Это чтобы часть пробелов не учитывать, а часть учитывать? В каком сценарии такое может быть нужно?


    1. nikabc555
      24.11.2015 14:40

      Это чтобы часть пробелов не учитывать, а часть учитывать? В каком сценарии такое может быть нужно?


      Эта функция реализована первоначально для того, чтобы не считать отступы слева при размещении текста в глубине данных с использованием «лестничной» конструкции.

      Например:

      level1+
        level2+
          level3+
            level4+
              text%
                >  Это красная строка.
                >А это обычная строка.
                >  Это еще одна красная строка.
                >     Это цитата, которая имеет свое количество отступов.
                >     И эти отступы легче посчитать от первого символа, чем от левого края.
              ^
            ^
          ^
        ^
      ^
      


      «часть пробелов не учитывать, а часть учитывать» — это вытекающий эффект из вышесказанного функционала.


      1. popov654
        24.11.2015 15:54

        Ну я кажется понял. Более простым языком выражаясь — чтобы вложенность (визуально) сохранить.


  1. baldr
    24.11.2015 19:04
    +1

    Уже есть же «Убийца-формат» Tree — устройте файтинг с ним!


    1. lair
      24.11.2015 20:04