33
nioで作ったBufferedWriter変えたら例外になった 2016-2-13 第十四回 #渋谷java

nioで作ったBufferedWriterに変えたら例外になった

Embed Size (px)

Citation preview

Page 1: nioで作ったBufferedWriterに変えたら例外になった

nioで作ったBufferedWriterに変えたら例外になった

2016-2-13第十四回 #渋谷java

Page 2: nioで作ったBufferedWriterに変えたら例外になった

自己紹介

島本 多可子(@chibochibo03)

株式会社ビズリーチ CTO室

普段はScalaを書いてます

GitBucketやってます

https://github.com/gitbucket/gitbucket

直近の著書です →

Page 3: nioで作ったBufferedWriterに変えたら例外になった

ある日

Page 4: nioで作ったBufferedWriterに変えたら例外になった

変更前

普通に生成していた

val charset: String = …

new BufferedWriter( new OutputStreamWriter( new FileOutputStream(new File("...")), charset ))

Page 5: nioで作ったBufferedWriterに変えたら例外になった

変更後

深く考えずnioのFiles.newBufferedWriterに変更

val charset: String = …

Files.newBufferedWriter( Paths.get("..."), Charset.forName(charset))

Page 6: nioで作ったBufferedWriterに変えたら例外になった

例外発生

Page 7: nioで作ったBufferedWriterに変えたら例外になった

Σ( ̄Д ̄;)なぬぅっ!!

Page 8: nioで作ったBufferedWriterに変えたら例外になった

java.nio.charset.UnmappableCharacterException

Page 9: nioで作ったBufferedWriterに変えたら例外になった

スタックトレース

Failed to import XML. java.nio.charset.UnmappableCharacterException: Input length = 1 at java.nio.charset.CoderResult.throwException(CoderResult.java:282) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:285) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.BufferedWriter.close(BufferedWriter.java:265)

Page 10: nioで作ったBufferedWriterに変えたら例外になった

(´へ`;ウーム

Page 11: nioで作ったBufferedWriterに変えたら例外になった

(━_━)ゝウーム

Page 12: nioで作ったBufferedWriterに変えたら例外になった

ハッ (゚Д゚;)!!

Page 13: nioで作ったBufferedWriterに変えたら例外になった

どうやら化ける文字に対する扱いが異なるようだ

Page 14: nioで作ったBufferedWriterに変えたら例外になった

こんなテストコードを用意

// 通常版

def normal() = { val charset = "EUC_JP" val data = "Java"+ "\u3030" +"Scala"

IO(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("...")), charset))).using { writer => IO { writer.write(data) writer.flush() } }}

Page 15: nioで作ったBufferedWriterに変えたら例外になった

こんなテストコードを用意

// 通常版

def normal() = { val charset = "EUC_JP" val data = "Java"+ "\u3030" +"Scala"

IO(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("...")), charset))).using { writer => IO { writer.write(data) writer.flush() } }}

Javaで、EUC_JPで出力不可な文字

Page 16: nioで作ったBufferedWriterに変えたら例外になった

結果

// 変換できない文字は'?'に置換

Java?Scala

Page 17: nioで作ったBufferedWriterに変えたら例外になった

nioの場合

// nio版def nio() = { val charset = "EUC_JP" val data = "Java"+ "\u3030" +"Scala"

IO(Files.newBufferedWriter(Paths.get("..."), Charset.forName(charset))).using { writer => IO { writer.write(data) writer.flush() } }}

Page 18: nioで作ったBufferedWriterに変えたら例外になった

結果

// 例外が発生し、出力もおかしなことに

JavaJava

Page 19: nioで作ったBufferedWriterに変えたら例外になった

(;¬д¬) アヤシイ

Page 20: nioで作ったBufferedWriterに変えたら例外になった

スタックトレース

Failed to import XML. java.nio.charset.UnmappableCharacterException: Input length = 1 at java.nio.charset.CoderResult.throwException(CoderResult.java:282) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:285) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.BufferedWriter.close(BufferedWriter.java:265)

Page 21: nioで作ったBufferedWriterに変えたら例外になった

StreamEncoder.implWrite

CharsetEncoder.encodeの結果次第

Page 22: nioで作ったBufferedWriterに変えたら例外になった

CharsetEncoderはどうやって決まる?

Page 23: nioで作ったBufferedWriterに変えたら例外になった

newBufferedWriterの場合

Page 24: nioで作ったBufferedWriterに変えたら例外になった

newBufferedWriterの場合

Page 25: nioで作ったBufferedWriterに変えたら例外になった

newBufferedWriterでたどり着く先

エンコードエラー時の動作は指定なし => CodingErrorAction.REPORT

Page 26: nioで作ったBufferedWriterに変えたら例外になった

CodingErrorAction.REPORTエラーが発生した時点で処理を中断し、エラーを報告する

Page 27: nioで作ったBufferedWriterに変えたら例外になった

従来の場合

Page 28: nioで作ったBufferedWriterに変えたら例外になった

従来でたどり着く先

不正入力エラー、マッピング不可エラー時の動作指定あり

Page 29: nioで作ったBufferedWriterに変えたら例外になった

CodingErrorAction.REPLACEエラーが発生した入力文字を指定した文字に置換して出力し、処理を継続する

デフォルトは「?」

設定されている置換文字列はreplacement メソッドで取得可能

Page 30: nioで作ったBufferedWriterに変えたら例外になった

(o゚∀゚)o キタ――♪

Page 31: nioで作ったBufferedWriterに変えたら例外になった

これが答え

Page 32: nioで作ったBufferedWriterに変えたら例外になった

CharsetEncoder.encode - 一部抜粋

newBufferedWriterの場合

従来の場合

Page 33: nioで作ったBufferedWriterに変えたら例外になった

まとめ

エンコードエラー時、置換文字による代替で処理を継続するか、エラーを通知してほしい

か、時と場合による

特に外部からのデータフィードの場合は、変な文字が紛れ込む

すぐに対応してもらえないことも・・・

nioのFiles.newBufferedWriterはエンコードエラー時に例外を投げるので注意