Разработка

Экранирование данных в CSV

Ранее мы рассматривали то, как можно в веб-приложении организовать экспорт данных в Excel, путём генерации файла CSV. Такой представляет собой всего лишь данные, разделённые запятыми (столбцы таблицы) и переносами строки (строки таблицы). Поэтому сгенерировать такой файл очень просто, но только до тех пор, пока данные таблицы сами не могут содержать пробелов и переносов строки.

Если же они их содержат (или хотя бы теоретически могут содержать), всё становится сложнее, потому что нужно думать об экранировании данных.

Используем кавычки

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

Страница, "Количество просмотров, тыс"

Если строка может содержать кавычки

Если же сама строка тоже может содержать кавычки, то внутри строки можно использовать сдвоенные кавычки.

Страница, "Количество ""хитов"", тыс"

Реализуем универсальное экранирование

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

const result = '"' + String(field) + '"'

При этом в содержимом ячейки мы находим все двойные кавычки и заменяем их на удвоенные кавычки:

const result = '"' + String(field).replace(/"/g, '""') + '"'

Итоговый код будет таким:

const viewsData = [
  ['Номер статьи','Количество "Хитов", тыс.'],
  [323,489],
  [324,156],
  [325,387]
]
const csvContent = viewsData.map((dataItem) => {
  return dataItem.map((field) => {
    return '"' + String(field).replace(/"/g, '""') + '"'
  })
  return `${dataItem.articleId},${dataItem.views}`
}).join('\n')
const link = document.createElement('a')
link.href = `data:text/csv;charset=utf-8,${csvContent}`
document.querySelector('body').appendChild(link)
link.textContent = 'Загрузить таблицу'

Используем библиотеку

Чтобы быть уверенными в том, что все нюансы, связанные с генерацией CSV и экранированием, учтены, лучше использовать какую-либо готовую библиотеку, например csv-stringify.

Тогда код для генерации ссылки для загрузки CSV будет гораздо проще:

const link = document.createElement('a')
link.href = `data:text/csv;charset=utf-8,${csv_stringify_sync.stringify(viewsData)}`
document.querySelector('body').appendChild(link)
link.textContent = 'Загрузить таблицу'