業務で「CSV書き込みユーティリティ」が必要になる理由
CSVは、外部ベンダーへのデータ提供、他システムへの連携、ユーザーへのエクスポート、バックアップなど、業務システムの「出口」としてめちゃくちゃよく使われます。 そのたびに「カンマをどうエスケープするか」「改行を含む値をどう書くか」「文字コードは何にするか」を毎回その場で考えていると、地味なバグと仕様差が積み重なっていきます。
だからこそ、「CSV書き込み」をユーティリティとしてきちんと切り出しておくと、 どの画面・どのバッチでも同じルールで CSV を出力できて、業務コードがすっきりし、トラブルも減ります。
CSVの「書き込みルール」をまず整理する
カンマ・改行・ダブルクォートの扱い
CSVの基本ルールは、読み込みのときと同じです。 しかし、書き込み側で意識しておくべきポイントを改めて整理します。
値の中にカンマがある場合、その値はダブルクォートで囲みます。 値の中に改行がある場合も、ダブルクォートで囲みます。 値の中にダブルクォートがある場合は、" を "" と二重にして書きます。
例えば、次のような値を CSV に書きたいとします。
「東京都,江戸川区」 「彼は”天才”と呼ばれた」
CSVとして正しく書くと、こうなります。
"東京都,江戸川区","彼は""天才""と呼ばれた"
このルールを守らないと、読み込み側が正しくパースできず、 列がずれたり、値が途中で切れたりします。
シンプルなCSV書き込みユーティリティ(まずは「割り切り版」)
「カンマも改行もダブルクォートも含まない」前提なら超シンプル
まずは、値がすべて「素直な文字列」である前提の、シンプルなユーティリティから見てみましょう。
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class SimpleCsvWriter {
public static void write(Path path,
Charset charset,
List<String[]> rows) throws IOException {
StringBuilder sb = new StringBuilder();
for (String[] cols : rows) {
for (int i = 0; i < cols.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(cols[i] == null ? "" : cols[i]);
}
sb.append("\r\n"); // 行末は CRLF にしておくと無難
}
Files.write(path, sb.toString().getBytes(charset));
}
public static void writeUtf8(Path path, List<String[]> rows) throws IOException {
write(path, StandardCharsets.UTF_8, rows);
}
}
Java使い方の例です。
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class SimpleCsvWriterExample {
public static void main(String[] args) throws Exception {
List<String[]> rows = new ArrayList<>();
rows.add(new String[] {"id", "name", "email"});
rows.add(new String[] {"1", "MONO", "mono@example.com"});
rows.add(new String[] {"2", "TARO", "taro@example.com"});
Path path = Path.of("out/simple.csv");
SimpleCsvWriter.writeUtf8(path, rows);
}
}
Javaここで重要なのは、「行末を明示的に \r\n にしている」ことです。 CSVはテキストなので、行末コードの違いで挙動が変わることがあります。 Windows系ツールとの連携を考えると、CRLF(\r\n)にしておくと無難です。
ちゃんとしたCSV書き込みユーティリティ(エスケープ対応)
値を「CSV用に整形する」関数を用意する
実務では、「カンマ・改行・ダブルクォートを含む値」を普通に扱う必要があります。 そこで、まず「1つの値をCSV用に整形する」関数を作ります。
public class CsvEscapeUtils {
public static String escape(String value) {
if (value == null) {
return "";
}
boolean needQuote =
value.contains(",") ||
value.contains("\"") ||
value.contains("\r") ||
value.contains("\n");
String escaped = value.replace("\"", "\"\"");
if (needQuote) {
return "\"" + escaped + "\"";
} else {
return escaped;
}
}
}
Javaここで深掘りしたいポイントは三つあります。
一つ目は、「どんなときにダブルクォートで囲むか」です。 カンマ、ダブルクォート、改行(\r や \n)を含む場合は、 その値全体を "..." で囲みます。
二つ目は、「ダブルクォートのエスケープ」です。 値の中に " がある場合、それを "" に置き換えます。 これが CSV の正式ルールです。
三つ目は、「null を空文字として扱う」ことです。 業務では、null をそのまま書くと "null" になってしまい、 読み込み側が「文字列としての ‘null’」と誤解することがあります。 ここでは、null は「空のセル」として扱っています。
エスケープ付きCSV書き込みユーティリティの全体像
行ごとに値をエスケープして連結する
先ほどの CsvEscapeUtils を使って、 CSV書き込みユーティリティを組み立てます。
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class CsvWriter {
public static void write(Path path,
Charset charset,
List<String[]> rows) throws IOException {
StringBuilder sb = new StringBuilder();
for (String[] cols : rows) {
for (int i = 0; i < cols.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(CsvEscapeUtils.escape(cols[i]));
}
sb.append("\r\n");
}
Files.write(path, sb.toString().getBytes(charset));
}
public static void writeUtf8(Path path, List<String[]> rows) throws IOException {
write(path, StandardCharsets.UTF_8, rows);
}
}
Java使い方の例です。
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class CsvWriterExample {
public static void main(String[] args) throws Exception {
List<String[]> rows = new ArrayList<>();
rows.add(new String[] {"id", "address", "note"});
rows.add(new String[] {
"1",
"東京都,江戸川区",
"彼は\"天才\"と呼ばれた\n2行目のメモ"
});
Path path = Path.of("out/escaped.csv");
CsvWriter.writeUtf8(path, rows);
}
}
Javaこのユーティリティを使うと、 カンマ・改行・ダブルクォートを含む値でも、 CSVとして正しく書き出せるようになります。
文字コードとBOMの扱いを意識する
文字コードは必ず明示する
CSV書き込みで一番やってはいけないのが、 文字コードを指定せずに getBytes() を使うことです。
text.getBytes() は、OSやJVMのデフォルト文字コードに依存します。 開発環境では動くのに、本番環境で文字化けする——という典型的な事故の原因になります。
業務では、ほぼ常に UTF-8 に統一するのがおすすめです。
Files.write(path, sb.toString().getBytes(StandardCharsets.UTF_8));
このように、必ず Charset を明示してください。
BOM(UTF-8 BOM)を付けるかどうかを方針として決める
UTF-8 には BOM が付く場合と付かない場合があります。 多くのシステムは「UTF-8 without BOM」を前提にしていますが、 Excel など一部のツールは BOM がある方が都合がよかったりします。
業務としては、次のような方針を決めておくと安全です。
外部システムとの連携仕様に「UTF-8(BOMなし)」か「UTF-8(BOMあり)」かを明記する。 ユーティリティ側で「BOMあり版」「BOMなし版」を切り替えられるようにする。
例えば、BOMありで書きたい場合は、先頭に BOM を付けます。
private static final byte[] UTF8_BOM = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
Files.write(path, combineBomAndData(UTF8_BOM, sb.toString(), charset));
private static byte[] combineBomAndData(byte[] bom, String text, Charset charset) {
byte[] data = text.getBytes(charset);
byte[] result = new byte[bom.length + data.length];
System.arraycopy(bom, 0, result, 0, bom.length);
System.arraycopy(data, 0, result, bom.length, data.length);
return result;
}
JavaCSV書き込みユーティリティの実務的な使いどころ
エクスポート機能(画面からのダウンロード)
管理画面から「CSVダウンロード」させる機能では、 CSV書き込みユーティリティがあるかどうかで、 品質と保守性が大きく変わります。
画面側は「どのデータを出すか」に集中し、 CSVの細かいルール(エスケープ・文字コード・行末コード)はユーティリティに任せる—— この分離ができていると、仕様変更にも強くなります。
外部ベンダーへのデータ提供
外部ベンダーに顧客データや売上データを渡すとき、 CSVの仕様(文字コード・BOM・ヘッダ行の有無・列順など)をきちんと守る必要があります。
CSV書き込みユーティリティを軸にしておくと、 「どの機能から出したCSVも同じ仕様である」状態を作りやすくなり、 ベンダーとのやり取りが安定します。
まとめ:CSV書き込みユーティリティで身につけてほしい感覚
CSV書き込みユーティリティは、「業務データを外の世界に正しく届ける」ための出口です。 そこには、次のような大事な感覚が詰まっています。
値の中のカンマ・改行・ダブルクォートを、CSVの正式ルールに従ってエスケープすること。 文字コード(UTF-8など)を必ず明示し、環境依存をなくすこと。 行末コード(\r\n など)を意識し、連携先の文化に合わせること。 BOM を付けるかどうかを業務として決め、ユーティリティで一貫させること。
もし今、あなたのプロジェクトで、 画面ごと・機能ごとにバラバラな CSV 出力コードが散らばっているなら、 それを一度「業務用 CSV書き込みユーティリティ」に集約できないか眺めてみてほしいです。
そこから先は、外部へのデータ提供やエクスポートが、 ぐっと安定して、信頼できるものになっていきます。
