poi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
poi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

7 Ocak 2016 Perşembe

Apache POI ve Excel

Aşağıda Apache POI ile ilgili aldığım notlar bulunuyor.
  • XSSFWorkbook xlsx formatındaki dosyaları okumak/yazmak için kullanılıyor.
  • org.apache.poi.ss.usermodel  paketi çoğu sınıf için arayüzleri içeriyor. Bu arayüzleri kullanan diğer sınıflar org.apache.poi.hssf.usermodel veya org.apache.poi.xssf.usermodel paketlerinde bulunuyor.
Bir çok örnek te http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/ss/examples/ConditionalFormats.java sayfasında mevcut.

WorkbookFactory
Dosya okuma veya yazma için bir Workbook açmak lazım. Bu HSSFWorkbook veya XSSFWorkbook olabilir.
  • POIFSFileSystem veya NPOIFSFileSystem sınıfları HSSFWorkbook formatında dosya açıyor. HSSFWorkbook eski Excel formatı olduğu için artık pek kullanılmıyor.
  • OPCPackage ise XSSFWorkbook formatında dosya açıyor.
Close Filehandle for Workbook (apache poi) sorusunda her bilinmeyen bir formattaki excel dosyasını açma örneği var.

Workbook
constructor
Workbook nesnesi yaratmak için stream vermemiz yeterli.
FileInputStream fin = new FileInputStream(new File("C:\\A2015.xlsx"));
XSSFWorkbook wb = new XSSFWorkbook(fin);
removeSheetAt
Örnekte ismi kritere uymayan sheet'ler siliniyor. Silmeye tersten başlamak lazım.
void removeOtherSheets(String sheetName, XSSFWorkbook book) {       
  for(int i=book.getNumberOfSheets()-1;i>=0;i--){
    XSSFSheet tmpSheet =book.getSheetAt(i);
    if(!tmpSheet.getSheetName().equals(sheetName)){
      book.removeSheetAt(i);
    }
  }       
}

XSSFSheet
Bu nesne WorkBook tarafından verilir.
XSSFSheet sheet = wb.getSheetAt(0);
Iterator
Bir sheet şöyle dolaşılır.
Iterator<Row> rowIt = sheet.rowIterator();
while (rowIt.hasNext()) {
  XSSFRow row = (XSSFRow) rowIt.next();

  // iterate through Columns
  Iterator<Cell> colIt = row.cellIterator();
  while (colIt.hasNext()) {
    Cell cell = colIt.next();
    System.out.println(cell.toString());
  }
  System.out.println();

}
Şöyle de dolaşılabilir. Bu dolaşmada null satırları atlamış oluruz.

for (Row row : sheet) {
  Cell cell = row.getCell(0);
  if (cell != null) {...}
}

Cell
toString metodu
Cell'in taşıdığı değeri string olarak döndürür.

Dosya Okuma 
xlsx Formatındaki Dosyayı Okumak
1. WorkbookFactory.create(OPCPackage pkg) metodunu kullanmak
Burada WorkBookFactory aslında XSSFWorkbook sınıfını yaratıyor.

FileInputStream fileInputStream = new FileInputStream(fileName);
//OPCPackage unzips .xlsx in order to read the xml
Workbook workBook = WorkbookFactory.create(OPCPackage.open(fileInputStream));
Sheet sheet = workBook.getSheetAt (0);

2. WorkbookFactory.create(InputStream inp) metodunu kullanmak
File inputFile = new File(fileName);
Workbook wb = WorkbookFactory.create(new FileInputStream(inputFile));

3. Arayüz kullanmadan XSSFWorkbook constructor metodlarından birisini kullanmak.

Şifre ile Korunan Bir Dosyayı Okumak
Burada bir örnek var.

xlsx Formatındaki Büyük Dosyayı SAX Parser Kullanarak Okumak
Gördüğüm en iyi örnek burada. XSSFReader sınıfını açıklayan örnek ise burada.

Okurken dikkat edilmesi gereken nokta ReadOnlySharedStringsTable sınıfı kullanılarak XSSFSheetXMLHandler sınıfına XML içindeki paylaşılan stringlerin geçilmesi. Bu tablo xlsx dokümaındaki \xl dizini altında bulunan sharedStrings.xml dokümanını kullanıyor.

 
xls Formatındaki Dosyayı Okumak
How do you get a cell's content in an Excel spreadsheet using Java? Apache POI  sorusuna verilen cevap aşağıda.

getNumberOfSheets metodu
Bu metod ile tüm sayfaları dolaşma imkanı var.
 

getCell metodu
Bu metodu kullanırken boş hücreler için aşağıdaki gibi Row.RETURN_BLANK_AS_NULL kullanabiliriz.

Dosyayı CellReference ile Okumak
getRow() ve getCell() metodları indeks olarak 0'dan başlayan sayılar alırlar. Ancak Excel'deki hücreler 1'den başlayan sayı alırlar. Örneği buradan aldım.

İsim verilen CellReference Aralıkları
Excel'de bazı hücre aralıklarına isim verilebiliyor. Bu isimleri görmek için aşağıdaki örneği kullanabiliriz.
XSSFName[] ranges = new XSSFName[workbook.getNumberOfNames()];
for (int i = 0; i < _wb.getNumberOfNames(); i++)
    ranges[i] = workbook.getNameAt(i);
   
AreaReference area = new AreaReference(ranges[0].getRefersToFormula());
//get all the cells within that range
CellReference[] cells = area.getAllReferencedCells();   
Dosyanın Tamamını Dolaşmak
Örneği buradan aldım. Burada dikkat edilmesi gereken nokta boş hücrelerin bazen xls dosyasına yazılmaması. Bu gibi durumlarda iterator yerine index kullanmak daha iyi. Buradaki soruda benzer bir cevap bulabilirsiniz
String fname = "C:\\myExcel.xlsx";
InputStream inp = new FileInputStream(fname);
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheetAt(0);

Iterator rows = sheet.rowIterator();
StringBuilder sb = new StringBuilder();
while (rows.hasNext()) {
    Row row = (Row) rows.next();
    Iterator<Cell> cells = row.cellIterator();
    while (cells.hasNext()) {
        Cell cell = cells.next();
        sb.append(cell.toString()).append(";");
    }
    sb.append("\n");
}
inp.close();
Dosyadaki Resimleri Okuma
Örnek:

Dosya Yazma
Büyük Dosyaları Yazma
Büyük dosyalar üretmek için http://poi.apache.org/spreadsheet/how-to.html adresinde örnek verildiği gibi SXSSFWorkbook sınıfını kullanmak lazım. Zaten bu sınıfın açıklamasında da "Streaming version of XSSFWorkbook implementing the "BigGridDemo" strategy. " yazıyor.


Eğer SXSSFWorkbook okuma işlemi için kullanılırsa burada gösterildiği gibi exception alırız.

Bir Dosya Oluşturma
Workbook workbook = WorkbookFactory.create(new File("file.xlsx"))
Boş bir dosya oluşturmak için aşağıdaki kod da kullanılabilir. openOrCreate() metodunun kullanıldığına dikkat etmek lazım.
OPCPackage opcPackage = OPCPackage.openOrCreate(file)
wb = new XSSFWorkbook(opcPackage);

Dosyaya AutoFilter Oluşturma
Örneği buradan aldım. İlk hücre başlığın olduğu yerden başlamalı ve son hücreye kadar devam etmeli.
sheet.setAutoFilter(CellRangeAddress.valueOf("A1:D10")); 

Bir Hücreye String Değer Atamak
SXSSFWorkbook workbook;//Yukarıdaki gibi oluştur
Sheet sheet = workbook.createSheet("sheet1");
Row row = sheet.createRow(9);//10. satır
Cell cell = row.createCell(3);//4 sütun
cell.setCellValue("label");
Bir Hücreye Sayısal Değer Atamak
Cell cell = row.createCell(3, Cell.CELL_TYPE_NUMERIC);//4 sütun
cell.setValue (100);
Bir Hücreye Formül Atamak
Cell cell = row.createCell(3, Cell.CELL_TYPE_FORMULA);//4 sütun
cell.setCellFormula ("SUM(D3:D4)");
Formüllerde $ işareti arkasındaki satır/sütun veya sayısı mutlak hale getirir. Örneğin $A1 formülü kopyalansa bile A sütunu sabit kalır. $A$1 şeklindeki kullanımda ise A1 hücresi sabit kalır.

Hücreye Style Atama
Hücreden Workbook'a Erişme
CellStyle nesnesi sadece Workbook tarafından yaratılabiliyor. Eğer Cell arayüzünden Workbook arayüzüne erişmek istersek aşağıdakine benzer bir kod kullanabiliriz.
CellStyle newCellStyle = newCell.getSheet().getWorkbook().createCellStyle();
Style Nesneleri 1 Kere Yaratılmalı
Bunun sebebini bilmiyorum ancak Style nesneleri bir kere yaratılmalı ve gerekirse bir yerde saklanmalı.

Style Nesneleri Hücrelere Atanmalı
Eğer bir satıra style atanırsa ve daha sonra o satırda bir hücre yaratılırsa, yeni hücre içinde bulunduğu satırın style'ını devralmaz. Hücreye style tekrar atanmalıdır.

Bir Hücreye Arka Plan Rengi Atamak
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());//yeşil renk
headerStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
Eğer istenirse indekslenmemiş bir renk te atanabilir. Örnek :
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(new XSSFColor(new Color(255, 255, 255)));//siyah renk
headerStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
Bir Hücreye Font Atamak
//create font
Font headerFont = workbook.createFont();
headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
headerFont.setFontHeightInPoints((short) 12);

//create ceyll style
CellStyle headerStyle = workbook.createCellStyle();
style.setFont(headerFont);

Cell cell = //create cell
cell.setCellStyle(headerStyle);
Formüller
FormulaEvaluator
Bir çok hücredeki değerleri değiştirdikten sonra tüm formülleri yeniden hesaplamak için aşağıdaki gibi yapabiliriz.

8 Ağustos 2012 Çarşamba

JExcel ve Apache POI

JExcel ve RowsExceededException
JExcel API'si eski olduğu için büyük excel dosyaları üretirken 65536 satırdan fazla satır yaratılmaya çalışılırsa RowsExceededException exception'ı atıyor. WritableSheetImpl.java dosyasındaki bu satır hatanın sebebi.

Büyük dosyalar üretmek için http://poi.apache.org/spreadsheet/how-to.html adresinde örnek verildiği gibi SXSSFWorkbook sınıfını kullanmak lazım. Zaten bu sınıfın açıklamasında da "Streaming version of XSSFWorkbook implementing the "BigGridDemo" strategy. " yazıyor.


JExcel ve Yuvarlama Hatası
JExcel kullanırken Number tipi alanların değerlerini getContents() metodu ile String olarak alırken dikkatli olmak lazım. Çünkü jxl.read.biff.NumberRecord sınıfı kendi içinde aşağıdaki gibi bir DecimalFormat nesnesi tutuyor. Ancak bu nesne sadece 3 hane hassasiyete kadar doğru değerleri döndürüyor. Eğer girilen değer daha fazla hassasiyete sahipse sonuç yuvarlanarak veriliyor ki bu da farkında olmadan hatalara sebep olabilir.

Aşağıda Jexcel'in kismi sınıf hiyerarşisi var.

Understanding the Excel .xls Binary File Format başlıklı yazıda da Excel'in veriyi nasıl sakladığı anlatılıyor.

XLSX Formatı
XLSX formatı aslında zip dosya formatı ile aynı.Eğer xlsx dosyasını bir zip programı ile açarsak karşımıza aşağıdaki görüntü çıkar.
XSSFSheetXMLHandler sınıfı ile  xml dosyasını parse etmek imkanı var. Bu sınıfın en önemli özelliği her hücreyi String olarak okuyabilmemizi sağlaması.

Excel Hücre Tipleri
Bir hücre aşağıdaki gibi Number olarak görünüyor olsak bile
hücrenin tipi SST_STRING olabiliyor. Bu durumda da string olarak okunabiliyor.

Eğer hücreye format verilmişse
hücrenin tipi NUMBER oluyor. XML içindeki veri 0.22556399999999999 olsa bile


double'a parse edilirken 0.225564 şekline dönüyor. Daha sonra formatlama stringi #,##0.000000 olduğu için java.text.DecimalFormat sınıfı sayesinde Türkçe 0,225564 haline geliyor.


POI ve Number hücrenin sağa/sola dayalı olması
Excel üzerinde 16,466166 görüyoruz ancak xml'de 16.466166000000001 de yazılı.
Eğer hücre sağa dayalı ise xml aşağıdaki gibi
<c r="A1"><v>16.466166000000001</v></c> --> çıktı 16.466166000000001





Eğer hücre sola dayalı ise xml aşağıdaki gibi
<c r="A2" s="1"><v>16.466166000000001</v></c> --> çıktı :  16,466166 çünkü s=1 ile style olarak "general format" kullanıyor