ExcelDataReaderで文字化けなくファイルを読み込む

日曜日 , 29, 9月 2019 Leave a comment

C#やVB .NETでExcelを使わずにExcelのファイルを読み込むときの定番NPOIを使用せずExcelDataReaderで文字化けなくファイルを読み込むための方法です。

ExcelDataReaderを用いて.xlsファイルを読み込み、文字化けを起こした例

  • NPOIは古い.xls形式のファイルを扱えない

とあるアプリケーションが吐き出した.xls形式のExcelファイルを扱う必要があり、NPOIを用いて読み込もうとしたとき、次のようなエラーが発生しました。
コード例とともに示します。

System.ArgumentException: ‘Your stream was neither an OLE2 stream, nor an OOXML stream.’

NPOIでSystem.ArgumentExceptionが発生してしまう.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
Using stream = New FileStream(xlsFilePath, FileMode.Open, FileAccess.Read)
 
    Dim xlWorkbook As IWorkbook = WorkbookFactory.Create(stream)
    Dim xlSheet As ISheet = xlWorkbook.GetSheetAt(0)
 
    For rowIndex = 0 To 10
        Dim xlRow As HSSFRow = xlSheet.GetRow(rowIndex)
        Console.WriteLine(xlRow.GetCell(0).StringCellValue)
    Next
 
    xlWorkbook.Close()
 
End Using

ICSharpCode.SharpZipLib.Zip.ZipException: ‘Wrong Local header signature: 0x40009’

NPOIでICSharpCode.SharpZipLib.Zip.ZipExceptionが発生してしまう.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
Using stream = New FileStream(xlsFilePath, FileMode.Open, FileAccess.Read)
 
    Dim xlWorkbook As XSSFWorkbook = New XSSFWorkbook(stream)
    Dim xlSheet As XSSFSheet = xlWorkbook.GetSheetAt(0)
 
    For rowIndex = 0 To 10
        Dim xlRow As HSSFRow = xlSheet.GetRow(rowIndex)
        Console.WriteLine(xlRow.GetCell(0).StringCellValue)
    Next
 
    xlWorkbook.Close()
 
End Using

原因を調べていくと.xlsファイルそのものに問題があるようです。
たしかに、Excelで開いても警告が表示され、操作に制限が課されるファイルだった(編集が行えない)ので、拡張子こそ.xlsでも中身は古いExcelファイル(BIFF5形式)だったようです。
NPOIはBIFF5形式のファイルはサポートしていないので今回のプログラムではNPOIの使用をあきらめざるを得ません。

そこでNPOIと並んでよく使われているExcelDataReaderを用いてファイルを読み込むことにしました。

  • ExcelDataReaderは文字化けを起こす

ExcelDataReaderを用いると、NPOIでは問題だったファイルもファイル自体は読み込むことができました。
しかし、データを細かく見ていくと日本語が格納されたセルは例外こそ発生しないものの100%文字化けが発生し、まともなデータを取り出すことができません。

インターネット上に公開されているサンプルの多くはExcelReaderFactoryクラスのCreateReaderメソッドでReaderを作成するときに、入力ストリームだけを引数として与える例が多いのですが、ここでExcelReaderConfigurationを用いてエンコーディングを指定してあげると文字化けが回避できるようです。
下記にコード例を示します。

文字化けしないExcelDataReader.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
' ExcelDataReaderを使用する際は明示的に呼び出さない例外が発生した(.NET Core 2.2)
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance)
    
' オプション設定でエンコーディングにシフトJISを指定
Dim config = New ExcelReaderConfiguration()
config.FallbackEncoding = System.Text.Encoding.GetEncoding(932)
Using stream = File.Open(xlsFilePath, FileMode.Open, FileAccess.Read)
    ' 引数にオプション設定をおこなう
    reader = ExcelReaderFactory.CreateReader(stream, config)
    ' データセットとして取得
    Dim ds = reader.AsDataSet
    Dim dtbl = ds.Tables(0)
    For Each dr As Data.DataRow In dtbl.Rows
        ' なにかの処理
        Console.WriteLine(dr.item(0))
    Next
        
End Using

オプションでエンコーディングを指定できることに気が付くまでは、データを取り出したあとでコンバートできないか試してみたのですが、取り出してしまったあとではどうにもならないようです。

また、文字化けについても、どの.xlsファイルを読み込んでも100%問題を起こすわけではなく、問題を起こす.xlsファイルを読み込んだ場合は100%文字化けが起きるというのが正しい状態説明のようです。