null

Обработка файлов .xls с помощью Apache POI

В ходе работы над различными программными продуктами часто возникает необходимость импорта и экспорта данных из различных "закрытых" форматов файлов. Чаще всего эта необходимость возникает применительно к файлам в форматах офисных продуктов корпорации Microsoft, в частности Word (doc, docx) и Excel (xls, xlsx). В силу особенностей реализации этих форматов, реализация такой обработки "в лоб" целиком своими силами была бы весьма нетривиальной и достаточно трудоёмкой задачей. К счастью, основная часть работы уже сделана за нас - существуют открытые Java-библиотеки, позволяющие преобразовать эти файлы в объектную модель Java, после чего получение из них необходимой нам информации не составит особого труда. В этой заметке показан пример реализации такой выборки данных из документа .xls с помощью Apache POI (на примере версии 3.6) - популярной библиотеки от Apache Software Foundation.

Из чего состоит документ Excel

Для начала, определимся с терминологией. Документ Excel (workbook) состоит из одного или более листов (или вкладок, в оригинале - sheets). Каждый лист представляет из себя матрицу из m строк (rows) по n ячеек (cells) в каждой. Максимальные размерности m и n различаются для разных версий Excel. В API POI каждому из структурных элементов файла Excel соответствует свой базовый интерфейс и его реализации для документов .xls и .xls, а работа с файловой системой осуществляется посредством класса org.apache.poi.poifs.filesystem.POIFSFileSystem.

Элемент

Базовый интерфейс

Реализация для .xls

Реализация для .xlsx

Документ, книга (Workbook)

org.apache.poi.ss.usermodel.Workbook

org.apache.poi.hssf.usermodel.HSSFWorkbook

org.apache.poi.xssf.usermodel.XSSFWorkbook

Страница, вкладка (Sheet)

org.apache.poi.ss.usermodel.Sheet

org.apache.poi.hssf.usermodel.HSSFSheet

org.apache.poi.xssf.usermodel.XSSFSheet

org.apache.poi.xssf.usermodel.XSSFChartSheet

org.apache.poi.xssf.usermodel.XSSFDialogsheet

Строка (Row)

org.apache.poi.ss.usermodel.Row

org.apache.poi.hssf.usermodel.HSSFRow

org.apache.poi.xssf.usermodel.XSSFRow

Ячейка (Cell)

org.apache.poi.ss.usermodel.Cell

org.apache.poi.hssf.usermodel.HSSFCell

org.apache.poi.xssf.usermodel.XSSFCell

Обратите внимание, что для страницы в документе Excel 2007 (.xlsx) существует сразу 3 реализации - помимо "общего" класса XSSFSheet добавлены XSSDialogSheet (страница-диалог) и XSSFChartSheet (страница, содержащая только график).

Алгоритм обработки

Таким образом, в общем случае, обработка документа Excel сводится к последовательному перебору ячеек и строк на каждой его странице. Значение каждой ячейки может быть преобразовано к нужному нам типу (строка, число, дата и т.д.) посредством методов, описанных в интерфейсе Cell и его реализациях.

Тип данных Метод Комментарий
CELL_TYPE_BLANK нет Используется для проверки того, пуста ли ячейка
CELL_TYPE_NUMERIC

getNumericCellValue()

Возвращает long

getDateCellValue()

Возвращает Date
CELL_TYPE_STRING

getStringCellValue()

Возвращает простую строку

getRichStringCellValue()

Используется для отформатированных строк. Возвращает RichTextString

getHyperlink()

Используется для получения гиперссылок. Возвращает Hyperlink
CELL_TYPE_FORMULA getCellFormula() Возвращает строку, содержащую формулу, в соответствии с которой формируется значение ячейки
CELL_TYPE_BOOLEAN getBooleanCellValue() Возвращает boolean
CELL_TYPE_ERROR getErrorCellValue() Возвращает код ошибки (byte)

Если тип содержимого ячейки не совпадает с тем типом данных, к которому мы пытаемся его преобразовать, будет сгенерировано исключение. Какое именно - зависит от конкретных типов данных между которыми мы пытаемся осуществить преобразование. К примеру, при попытке получения числового значения из строковой ячейки, будет сгенерирован java.lang.IllegalStateException. Для того, чтобы избежать исключительной ситуации, можно заранее проверить, к какому типу относится содержимое конкретной ячейки - для этой цели существует метод getCellType(), объявленный всё в том же интерфейсе Cell.

Пример разбора файла

Теперь перейдём от теории к практике - рассмотрим на примере, как может выглядеть код простейшей программы на Java, получающей данные из документа xls посредством API Apache POI. Предположим, что мы хотим выбрать данные из некоего справочника контактной информации, который выглядит примерно так:

Таким образом, наш документ состоит из трёх столбцов - ФИО, Адрес и Телефон. Чтобы упростить задачу, создадим класс с теми же самыми полями, который будет хранить информацию о контактных лицах:

package com.tuneit.poi.sample;

/*
 * Класс, предназначенный для хранения
 * перснональной информации контактного лица
 */
public class ContactPerson {

        private String name; //ФИО
        private String address; //Адрес
        private String phoneNumber; //Номер телефона

        //Getters & setters
        ...
}

Это простейший POJO-класс, содержащий только поля (соответствующие столбцам нашего xls-документа) и get- и set-методы к ним (в листинге не приведены).

Теперь рассмотрим основной фрагмент кода - собственно, сам парсер содержимого документа:

public static final NAME_COLUMN_NUMBER = 0; //ФИО
public static final ADDRESS_COLUMN_NUMBER = 1; //Адрес
public static final PHONE_COLUMN_NUMBER = 2; //Телефон

...

public List<Person> getContacts(String path){
        List<Person> contacts = new ArrayList<Person>(); //Создаём пустой список контактов

        File addressDB = new File(path); //Переменная path содержит путь к документу в ФС
        POIFSFileSystem fileSystem =   new POIFSFileSystem(addressDB); //Открываем документ
        HSSFWorkbook workBook = new HSSFWorkbook(fileSystem); // Получаем workbook
        HSSFSheet sheet = workBook.getSheetAt(0); // Проверяем только первую страницу

        Iterator<Row> rows = sheet.rowIterator(); // Перебираем все строки

        // Пропускаем "шапку" таблицы
        if (rows.hasNext()) {
                rows.next();
        }

        // Перебираем все строки начиная со второй до тех пор, пока документ не закончится 
        while (rows.hasNext()) {
                HSSFRow row = (HSSFRow) rows.next();
                //Получаем ячейки из строки по номерам столбцов
                HSSFCell nameCell = row.getCell(NAME_COLUMN_NUMBER); //ФИО
                HSSFCell addressCell = row.getCell(ADDRESS_COLUMN_NUMBER); //Адрес
                HSSFCell phoneCell = row.getCell(PHONE_COLUMN_NUMBER); //Номер телефона
                // Если в первом столбце нет данных, то контакт не создаём 
                if (nameCell != null) {
                        Person person = new Person();
                        person.setName(nameCell.getStringCellValue()); //Получаем строковое значение из ячейки

                        person.setAddress(""); //Адрес может не быть задан
                        if (addressCell != null && !"".equals(addressCell.getStringCellValue())) {
                                person.setAddress(addressCell.getStringCellValue()); //Адрес - строка
                        }

                        person.setPhone(""); //Телефон тоже может не быть задан
                        if (phoneCell != null && !"".equals(phoneCell.getStringCellValue())) {
                                person.setPhoneNumber(phoneCell.getStringCellValue()); // Телефон - тоже строка
                        }

                        contacts.add(person); //Добавляем контакт в список

                }
        }
        return contacts;
}

Таким образом, на выходе приведённого выше метода мы получим коллекцию объектов класса Person, сформированных из данных исходного xls-файла. Разумеется, в случае решения более общей задачи - например, написания универсального обработчика xls-документов, логика программы усложнится, но её основа, скорее всего, останется прежней - всё тот же последовательный перебор всех строк таблицы.

Коротко о себе:

Работаю ведущим программистом в компании Tune IT и ассистентом кафедры Вычислительной техники в Университете ИТМО .

Занимаюсь проектами, связанными с разработкой разного рода веб-приложений (порталы, CRM-системы, системы электронного документооборота), а также, в рамках научной работы на кафедре, изучаю возможности применения семантического анализа в задачах САПР.

Ничего не найдено. n is 0