4
April

Чтение и запись CSV файлов в Java (comma separated values file format)

Posted in: Java open-source проекты, Java technologies, J2SE, Полезные программы |

Решил сделать свой вклад в мировое open-source сообщество. Как вы уже догадались из названия статьи, на этот раз речь пойдет о формате CSV.

Итак, проблема заключалась в том, что генерация отчетов в формате Excel через библиотеку POI кушала много оперативной памяти и процессорного времени.

Сам по себе Excel, наверное, один из немногих хороших продуктов компании Microsoft, но вот формат файлов оставляет желать лучшего. Короче говоря, при генерации документа необходимо полностью держать его в памяти.

Выходом из ситуации является использование формата CSV (comma-separated values file format), который к счастью тоже читается с помощью Excel.

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

Сразу хочу обратить ваше внимание на то, что стандартное поведение библиотеки рассчитано на чтение и запись CSV файлов, которые понимает Excel, так как это самый распространенный вариант использования CSV файлов.

Все различия между Excel CSV и Pure CSV вынесены в удобно конфигурируемые свойства:
delimiter - разделитель, по умолчанию - “;”
preserveSpaces - сохранять ли пробелы при чтении, по умолчанию - “true”
ignoreEmptyLines - игнорировать ли пустые линии (то есть те, в которых нет значений и разделителей), по умолчанию - “false”
ignoreComments - игнорировать ли комментарии, по умолчанию - “false”

Изначально, конечно же, очень хотелось наворотить библиотеку всякими “полезными” функциями: например, поддержкой заголовков, Unix-подобным эскейпингом и т.д.. Но разум взял верх и получилась, на мой взгляд, вполне хорошая библиотека без лишнего мусора.

Ниже представлены примеры кода на Java с использованием библиотеки.

Для создания и записи в CSV файл:

  Csv.Writer writer = new Csv.Writer("filename").delimiter(',');
  writer.comment("example of csv")
      .value("a").value("b").newLine()
      .value("c").close();

В результате выполнения этого кода будет сгенерирован следующий файл:

#example of csv
a,b
c

А это вариант для чтения данных из CSV (предположим, что мы читаем файл сгенерированный в предыдущем примере):

  Csv.Reader reader = new Csv.Reader(new FileReader("filename"))
      .delimiter(',').ignoreComments(true);
  System.out.println(reader.readLine());

Результат будет таким:

[a, b]

Для более изощренных вариантов использования посмотрите файл CsvTestCase из папки src библиотеки. В jar этот тест кейс я не вносил, чтобы не создавать лишние dependency на библиотеку.

Библиотека является свободной для использования и распространения. Скачать ее можно здесь - Java CSV Library

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

Updated (07.04.2008): Смешно, конечно, но уже 3-ий раз меняю логику обработки исключений в библиотеке, а точнее имена и иерархию исключений. При этом остальной код остался без изменений.

Итак, базовое исключение для всех специфичных для библиотеки ошибок - Csv.Exception (extends RuntimeException). Его можно ловить и игнорировать в случае, если это исключение не критично для дальнейшей работы приложения.

При возникновении ошибки связанной с форматом CSV бросается Csv.FormatException (extends Csv.Exception). Если эта ошибка возникла при записи в файл, то ловить ее не имеет смысла (это значит,что программист неправильно генерирует файл). Если же ошибка возникла при чтении, то ловить или не ловить ее решает программист.

При возникновении java.io.IOException она оборачивается в Csv.IOException (exstends Csv.Exception). Ловить или не ловить исключение решает программист. Ловить ее можно и нужно, например, при чтении/записи CSV файла по сети. В этом случае можно переконнектиться и начать чтение/запись заново. Если она возникла при других условиях, то тут уже ничего не поделаешь…

6 Comments »

RSS feed for comments on this post.



April 4, 2008 #

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

З.Ы. Понравился файл с лицензией ) Имхо можно было бы выбрать что-нибудь стандартное вроде BSDL. Впрочем ваш код - ваше право.

З.Ы.Ы. Спасибо за библиотеку.

April 4, 2008 #

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

На счет лицензирования: К сожалению эту область я еще не изучал.

На счет Exception: Мне больше нравится использовать unchecked exceptions, но только в том случае, если ситуация действительно исключительная и не может быть обработана пользователем класса в большинстве случаев.

В случае с невалидным CSV пользователь ничего не может сделать: точнее при записи в CSV он должен переписать свой неправильный код, а при чтении - отказаться от парсинга невалидного файла.

Единственное, на счет чего я сомневаюсь - то что для ошибок невалидности формата я использовал тот же Exception, что и для обертки IOException.
Наверное для ошибок формата лучше бросать IllegalStateException с соответствующим текстом.
Ну а IOException я оборачиваю в RuntimeException для того, чтобы пользователь моего класса не матюкался добавляя в своих методах некрасивые конструкции типа throws или try/catch/finally :).
Если программист посчитает нужным ловить Csv.Exception, он напишет соответствующий код. Но ловить ее надо только в случае чтения/записи CSV через сеть, так как только в этом случае можно попробовать корректно решить проблему - переконнектится и дальше совершить чтение/запись.

April 5, 2008 #

Хм… я сторонник несколько иной стратегии использования исключений. Хотя может быть я не понимаю всего смысла. Предположим что возникнет рантайм-эксепшн в обычном однопоточном приложении. Оно не прибьется?

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

Alexander
April 8, 2008 #

Здравствуйте! А подскажите, пожалуйста, если я все 4 параметра подставлю по умолчанию, получится Excel или Pure CSV?

April 8, 2008 #

Стандартное поведение библиотеки рассчитано на чтение и запись CSV файлов, которые понимает Excel.

Крым
April 16, 2008 #

Спасибо за коды, возможно пригодиться, завтра дам компьютерику на фирме.
А вот нащёт лицензирования вы зря так. Изучайте если можете создавать подобные доработки!
Напишу завтра отзыв своего хакера, он вечно чем то недоволен в Excel, может как раз то что ему нужно.

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>