本番環境で見られるエッジケースを試しています。クライアントがテキスト ファイルを生成し、それを FTP でサーバーに送信するビジネス モデルがあります。これらのファイルを取り込み、Java バックエンド (CentOS マシンで実行) で処理します。クライアントのほとんど (95% 以上) は、これらのファイルを必要な UTF-8 で生成することを知っています。ただし、CP1252 文字セットを使用して Windows マシン上でこれらのファイルを生成する頑固なクライアント (しかし大規模なアカウント) がいくつかあります。問題ありませんが、魔法のブードゥーを通じて任意の文字セットで入力を処理するように、サードパーティのライブラリ (ほとんどの「処理」作業を行うもの) を構成しました。
時折、名前に不正な UTF-8 文字 (CP1252) が含まれているファイルを目にすることがあります。私たちのソフトウェアが FTP サーバーからこれらのファイルを読み取ろうとすると、ファイル読み取りの通常の方法はチョークし、次のメッセージをスローしFileNotFoundException
ます。
File f = getFileFromFTPServer();
FileReader fReader = new FileReader(f);
String line = fReader.readLine();
// ...etc.
例外は次のようになります。
java.io.FileNotFoundException: /path/to/file/some-text-blah?blah.xml (No such file or directory) at java.io.FileInputStream.open(Native Method) at
java.io.FileInputStream.(FileInputStream.java:120) at java.io.FileReader.(FileReader.java:55) at com.myorg.backend.app.InputFileProcessor.run(InputFileProcessor.java:60) at
java.lang.Thread.run(Thread.java:662)
つまり、ファイル名自体に不正な文字が含まれているため、そもそもそれを読み取ることさえできないということです。可能であれば、ファイルの内容に関係なく、ソフトウェアはそれを正しく処理できるはずです。したがって、これは実際には、不正な UTF-8 文字を含むファイル名を読み取る際の問題です。
テスト ケースとして、非常に単純な Java "アプリ" を作成して、サーバーの 1 つにデプロイし、いくつかのことをテストしました (ソース コードを以下に示します)。次に、Windows マシンにログインし、テスト ファイルを作成して名前を付けましたtest£.txt
。ファイル名の「test」の後の文字に注意してください。これは Alt-0163 です。これを私たちのサーバーに FTP 送信し、ls -ltr
その親ディレクトリで実行したとき、それが としてリストされているのを見て驚きましたtest?.txt
。
先に進む前に、この問題をテスト/再現するために作成した Java "アプリ" を次に示します。
public Driver {
public static void main(String[] args) {
Driver d = new Driver();
d.run(args[0]); // I know this is bad, but its fine for our purposes here
}
private void run(String fileName) {
InputStreamReader isr = null;
BufferedReader buffReader = null;
FileInputStream fis = null;
String firstLineOfFile = "default";
System.out.println("Processing " + fileName);
try {
System.out.println("Attempting UTF-8...");
fis = new FileInputStream(fileName);
isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
buffReader = new BufferedReader(isr);
firstLineOfFile = buffReader.readLine();
System.out.println("UTF-8 worked and first line of file is : " + firstLineOfFile);
}
catch(IOException io1) {
// UTF-8 failed; try CP1252.
try {
System.out.println("UTF-8 failed. Attempting Windows-1252...(" + io1.getMessage() + ")");
fis = new FileInputStream(fileName);
// I've also tried variations "WINDOWS-1252", "Windows-1252", "CP1252", "Cp1252", "cp1252"
isr = new InputStreamReader(fis, Charset.forName("windows-1252"));
buffReader = new BufferedReader(isr);
firstLineOfFile = buffReader.readLine();
System.out.println("Windows-1252 worked and first line of file is : " + firstLineOfFile);
}
catch(IOException io2) {
// Both UTF-8 and CP1252 failed...
System.out.println("Both UTF-8 and Windows-1252 failed. Could not read file. (" + io2.getMessage() + ")");
}
}
}
}
これをターミナル ( java -cp . com/Driver t*
) から実行すると、次の出力が得られます。
Processing test�.txt
Attempting UTF-8...
UTF-8 failed. Attempting Windows-1252...(test�.txt (No such file or directory))
Both UTF-8 and Windows-1252 failed. Could not read file.(test�.txt (No such file or directory))
test�.txt
?!?! 調査を行ったところ、「�」が Unicode 置換文字であることがわかりました\uFFFD
。したがって、CentOS FTP サーバーが Alt-0163 ( ) の処理方法を認識していないため、 ( )に置き換えられていることが原因だと思います。しかし、なぜ...というファイルが表示されるのかわかりません。£
\uFFFD
�
ls -ltr
test?.txt
いずれにせよ、解決策は、ファイル名にこの文字が含まれているかどうかを検索するロジックを追加し、見つかった場合はファイルの名前を別の名前に変更することであるようです (おそらく、文字列ごとreplaceAll("\uFFFD", "_")
など)。システムが読み取って処理できること。
問題は、Java がファイル システム上のこのファイルを認識さえしないことです。CentOS はファイルがそこにある ( test?.txt
) ことを認識していますが、そのファイルが Java に渡されると、Java はそれtest�.txt
を何らかの理由でNo such file or directory
...と解釈します。
Java にこのファイルを表示させて、実行できるようにするにはどうすればよいFile::renameTo(String)
ですか? ここでの裏話で申し訳ありませんが、このシナリオではすべての詳細が重要であるため、関連性があると思います. 前もって感謝します!