
PTTJS - plain text table javascript, формат разработанный из личной необходимости и острой нужды.
Публикую в народ, потому что уверен, что не меня одного волнуют ограничения и проблемы текущих форматов для табличных данных.
Уже написаны JS библиотека с парсером и сериализатором, а также Obsidian плагин.
Цель
Главная цель формата PTTJS - получить текстовый формат таблиц, который позволяет хранить более сложные таблицы, нежели существующие форматы (CSV, MD), но при этом сохранять читаемость и текстовую основу.
Мотивация
Мотивация для разработки копилась в течении долгого времени, ведь Я как и все нормальные люди готов мириться с некоторыми неудобствами. Но недавно терпение кончилось и пришлось взяться за проработку и разработку.
Решающими стали факторы:
Необходимость передавать в LLM более сложные таблицы нежели обычные CSV или MD форматы. Очень часто в документах встречаются сложносоставные таблицы, которые невозможно перевести в текстовый формат. Из-за чего приходится использовать более сложные и тяжелые мультимодальные модели.
Необходимость распознавать сложносоставные таблицы с помощью CV возвращая простой текст, а ни какой-нибудь многовложенный JSON, который ещё нормально не перепроверишь, пока не отрендеришь.
Необходимость обучать LLM работе целиком с таблицей, а не через костыли внутри Google Shets или Microsoft Excel. Дать возможность LLM-ке самой крутить стобцы, строки, формулы, как ей понадобится.
Невозможность открыть большинство, даже простых таблиц, которые, за неимением аналогов, все хранят в XLS(X) или ODT, без специального ПО.
Желание работать с более сложными таблицами в текстовых редакторах и ПО по типу Obsidian.
Личная неприязнь к Google Shets и Microsoft Excel (надеюсь Я не один такой).

Описание формата
Первая строка всегда отдана под аннотацию:
|PTTJS 1.0|encoding=UTF‑8|\n
Страницы
Каждая таблица лежит на странице:
|(@P1|Название страницы){\n
- начало страницы, в скобках: id страницы начинается с символа "@" и название страницы.
}|\n
- конец страницы.
Обозначения страницы опциональны, если их нет, вся таблица на одной странице.
Ячейки
Вся таблица состоит из ячеек и обозначения конца строки:
|H([1|1]1|1|@C1)>
- начало ячейки, H - пометка, что это заголовок, в круглых скобках: индекс ячейки по X и Y в квадратных скобках, масштаб (scale) ячейки по X и Y и id ячейки (начинается с символа "@").
<|\n
- конец строки.
H - опциональная пометка, что ячейка - это заголовок.
Индекс ячейки по X и Y - опционален, при обработке просто заполняется итеративно по ячейкам и строкам, начиная с [0|0]. Можно выгрузить при необходимости не визуальной обработки (в скрипте или c помощью AI).
Масштаб (scale) ячейки по X и Y - опционален и по умолчанию равен 1|1.
Масштаб всегда указывается в верхней левой ячейке.
Id ячейки - опционален, нужен для ссылок и направленных формул.
Даже если в строке нет значений, но после неё есть строки, надо указать в строке хотя бы одну пустую ячейку |><|
.
Данные внутри ячеек не должны содержать символов ответственных за указание новой ячейки, конца строки, фигурных скобочек и переноса строки ("\n", "|", ">", "<", "{", "}").
Все эти символы должны быть заменены на URL Encode аналоги:
Новая строка (\n
) -> %5Cn
Вертикальная черта (|
) -> %7C
>
-> %3E
,<
-> %3C
,{
-> %7B
,}
-> %7D
,
чтобы парсер валидно воспринимал данные.
В библиотеке в serializer.js есть функция escapeValue
, которая может заменить символы на URL Encode вариант.
В библиотеке в parser.js есть функция unescapeValue
, которая может заменить символы обратно из URL Encode варианта в обычный вид.
Скрипты
>>>SCRIPT\n
- начало блока скриптов
<<<SCRIPT\n
- конец блока скриптов
Блок скриптов всегда находится после всех данных и может работать с данными на разных листах.
Скрипты хранят формулы и форматирование, которые срабатывают при инициализации или изменении таблицы в совместимом редакторе.
Обработка ячеек делается с помощью JS кода и специально заготовленных JS функций.
Можно определять свои функции в редакторе.
Стили присваиваются с помощью символа "<=" и описываются в CSS подобном формате (тут также будут срабатывать JS функции для стилизации).
Форматы ячеек присваиваются символом "=>" и указывают на формат данных в ячейках (это тоже JS функции).
Например NUMBER(2,' '), первый параметр это decimals - количество цифр после запятой, а второй - разделитель тысяч, в нашем случае пробел ' '.
Можно добавлять скрипты для всей строки или столбца или вообще всего диапазона с помощью двойного указания индекса через двоеточие ":", например вся первая строка (0:0|0), или весь первый столбец (0|0:0) и т.д.
Скрипты это опциональный блок.
В ячейках всегда хранится актуальная информация. Когда мы добавляем скрипт, он пересчитывает данные таблицы и складывает по ячейкам актуальную информацию.
При выгрузке - скрипты можно сохранять отдельным файлом, например с суффиксом ".script".
Примеры
Пример простой таблицы, например для хранения данных о машинах:
|PTTJS 1.0|
|H>Номер машины|H>Год выпуска|H>Марка модель<|
|>080XXX02|>2005|>LEXUS RX 350<|
|>787XXX16|>2015|>GEELY GC7<|
|>871XXX05|>1997|>TOYOTA IPSUM<|
|>A602XXX|>1996|>MITSUBISHI PAJERO<|
|>890XXX02|>1997|>TOYOTA LAND CRUISER PRADO<|
|>216XXX13|>2007|>DAEWOO NEXIA<|
Пример простой таблицы, c индексами:
|PTTJS 1.0|encoding=UTF‑8|
|H([0|0])>Номер машины|H([1|0])>Год выпуска|H([2|0])>Марка модель<|
|([0|1])>080XXX02|([1|1])>2007|([2|1])>LEXUS RX 350<|
|([0|2])>787XXX16|([1|2])>2015|([2|2])>GEELY GC7<|
|([0|3])>871XXX05|([1|3])>1997|([2|3])>TOYOTA IPSUM<|
|([0|4])>A602XXX|([1|4])>1996|([2|4])>MITSUBISHI PAJERO<|
|([0|5])>890XXX02|([1|5])>1997|([2|5])>TOYOTA LAND CRUISER PRADO<|
|([0|6])>216XXX13|([1|6])>2007|([2|6])>DAEWOO NEXIA<|
Пример таблицы cо "сложным" заголовком:
|PTTJS 1.0|encoding=UTF‑8|
|H(1|2)>Номер машины|H(2|1)>Данные о машине|H><|
|H>|H>Год выпуска|H>Марка модель<|
|>080XXX02|>2007|>LEXUS RX 350<|
|>787XXX16|>2015|>GEELY GC7<|
|>871XXX05|>1997|>TOYOTA IPSUM<|
|>A602XXX|>1996|>MITSUBISHI PAJERO<|
|>890XXX02|>1997|>TOYOTA LAND CRUISER PRADO<|
|>216XXX13|>2007|>DAEWOO NEXIA<|

В этом примере объединены первые ячейки первых двух строк, с заголовком "номер машины" и объединены вторая и третья ячейки первой строки с заголовком "Данные о машине".
Объединенные ячейки остаются. Это нужно чтобы не потерять позиционную информацию.
Пример простой таблицы, c индексами и скриптами:
|PTTJS 1.0|encoding=UTF‑8|
|H([0|0])>Номер машины|H([1|0])>Год выпуска|H([2|0])>Марка модель<|
|([0|1])>080XXX02|([1|1])>2007|([2|1])>LEXUS RX 350<|
|([0|2])>787XXX16|([1|2])>2015|([2|2])>GEELY GC7<|
|([0|3])>871XXX05|([1|3])>1997|([2|3])>TOYOTA IPSUM<|
|([0|4])>A602XXX|([1|4])>1996|([2|4])>MITSUBISHI PAJERO<|
|([0|5])>890XXX02|([1|5])>1997|([2|5])>TOYOTA LAND CRUISER PRADO<|
|([0|6])>216XXX13|([1|6])>2007|([2|6])>DAEWOO NEXIA<|
|([0|7])><|
|([0|8])>Средний год выпуска|([1|8])>2003<|
>>>SCRIPT
(1|1,1|6)=>NUMBER(2,' ')
(1|8)=DIV(SUM(1|1,1|6),COUNT(1|1,1|6))
(0|8:8)<=BORDER(each,2,solid,#000)
<<<SCRIPT
В этом примере у нас есть девятая строка, во второй ячейке которой лежит формула среднего значения диапазона ячеек.
Ещё указаны форматы ячеек во втором столбце со 2 по 7 строках, это числовой формат.
Также мы присвоили CSS стили диапазону ячеек, а именно обводку каждой ячейки черного цвета и толщиной 2.
Планы
Предстоит доделать срабатывание функций из блока скриптов на объекте Store, который получается после парсинга.
Добавить функции преобразования форматов (XSL(S), ODT, CSV, MD <-> PTTJS)
Добавить библиотеки для других языков.
Сделать легкий веб интерфейс для работы с PTTJS.
Сотрудничество (контрибьютинг)
GitHub репозиторий открыт, можете предлагать изменения.
Заключение
На этом всё, спасибо что уделили время, надеюсь смог помочь решить чьи-то аналогичные проблемы.
Комментарии (14)
aborouhin
11.05.2025 17:51Задачу читабельного текстового представления достаточно сложных таблиц неплохо решает AsciiDoc. В нём из коробки нет форматов данных и формул, но есть механизм расширений, который, насколько я понимаю, позволит это добавить. При этом можно было бы использовать уже существующие инструменты для конвертации из Excel, преобразования в кучу форматов и пр.
ncix
Без обид, но мне кажется даже жуткий xml в xlsx лучше читается
А зачем эти угловые скобки? Вертикальные палки и так отделяют ячейки
Почему именно три угловые скобки? А что если к скрипту понадобятся атрибуты? Язык, версия и т.п.
Может проще прямо на JS описывать таблицы любой сложности?
kolkoni Автор
Как вы читаете бинарный xlsx?
Ответ простой - Я так решил.
В статье написано.
потом поделитесь счетом, сколько ушло денег на скрамливание XML в LLM. При чем тут LLM написано в статье.
Нет. И даже в HTML нет.
ncix
XLSX - это обычный zip-архив, внутри папки, XML-ки, картинки и прочий контент. Табличные данные хранятся в XML.
kolkoni Автор
И вы прям в текстовом редакторе его открываете и читаете? Или нужно сначала ряд операций провести?
Можно пример Вашего более читаемого xml в xlsx? Вот ту же таблицу из моего примера.
aborouhin
Ещё бы в нём строковые значения за каким-то лешим не были вынесены в отдельный файл, в котором их надо подсматривать по индексам из основного файла листа, - вот тогда читался бы он нормально :)
ncix
поэтому - жуткий ))