2025, Dec 17 03:02

Как удалить комментарии из JSONC на Python, не ломая строки

Как удалить комментарии из JSONC на Python с помощью регулярных выражений: сохраняем строки в кавычках, убираем // и /*...*/, получаем валидный JSON быстро.

Удалять комментарии из JSONC, чтобы получить валидный JSON, кажется простым делом — пока не вмешиваются строковые литералы. В URL, путях и других значениях встречаются двойные слэши, поэтому наивное регулярное выражение, которое вычеркивает всё от // до конца строки, повредит данные. Ниже — надёжный подход на Python, основанный только на регулярках: он сохраняет строковые значения в кавычках и при этом убирает комментарии.

Постановка задачи

На входе — JSONC, надстройка над JSON, допускающая комментарии. Задача — удалить комментарии и получить корректный JSON, используя только регулярные выражения. Подводный камень: // может встречаться внутри строк в кавычках, и прямолинейный шаблон удалит лишнее.

Пример, показывающий проблему

Сначала — наивная попытка, которая ломается, если // встречается внутри строковых значений:

import re
bad_rule = re.compile(r'\s//[^}]*')
result = bad_rule.sub('', data)

Посмотрим на такой ввод. Это корректный JSONC, аккуратно отформатированный. Если удалить комментарии правильно, он гарантированно превратится в валидный JSON:

// пробовал это sed -r 's#\s//[^}]*##'
//  пробовал и это '%^*3//s39()'
[
  {
    "test1" : "http://test.com",
    "test2" : "http://test.com",//тест
    // что угодно
    "ok" : 3,  // здесь 2
    "//networkpath1" : true, //почему бы и нет
    "//networkpath2" : true 
// ок
  },// конец файла
  {
    "statement" : "I like test cases"
}// конец файла
]

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

Почему наивный подход ломается

Регулярка, которая без разбора хватается за // до некой границы, не учитывает грамматику строковых литералов. Если // встречается внутри кавычек, это не начало комментария, но шаблон всё равно его находит и удаляет корректное содержимое. Такая же опасность и с блочными комментариями: последовательности /* и */ тоже могут попадаться внутри строк.

Регулярка, которая сохраняет строки и удаляет комментарии

Рабочая стратегия — одновременно сопоставлять комментарии и строковые литералы: строки захватывать в группу, а при замене подставлять их обратно. Так строки остаются нетронутыми, а построчные и блочные комментарии удаляются. Подход не зависит ни от отступов, ни от конкретных переводов строк.

import re
rx_cleanup = re.compile(
    r'//.*|/\*[\\s\\S]*?\\*/|("(\\\\\.|.)*?")'
)
sanitized = rx_cleanup.sub(r'\\1', data)

Шаблон чередует три части. Он находит комментарии // до конца строки. Он находит блочные фрагменты /* ... */ с помощью конструкции, охватывающей любые символы. И, что важно, он распознаёт строку в кавычках как захватывающую группу, допускающую экранированные символы внутри; при замене эта группа подставляется обратно, так что содержимое в кавычках сохраняется, а комментарии исчезают.

Почему это важно

Удаление комментариев из JSONC с помощью регулярных выражений удобно для быстрого препроцессинга, но корректность держится на том, чтобы не трогать строковые значения. В URL и подобных данных часто встречается //, и любое случайное удаление ведёт к невалидному JSON или поломанной семантике. Сохранение содержимого в кавычках при одновременном удалении комментариев решает задачу без привязки к форматированию.

Итоги

Если приходится конвертировать JSONC в JSON при помощи регулярок, защитите строки в кавычках и подставляйте их обратно при замене. Показанный шаблон обрабатывает и //, и /* ... */-комментарии (даже если нужен лишь первый вид) и не полагается на конкретные отступы или тип перевода строки. При гарантии, что после удаления комментариев получится валидный JSON, этот метод даёт краткое и практичное решение.