改行を含むTSVファイルを使ってデータ結合を行う方法

InDesignのデータ結合では、フィールドデータに改行が含まれているとデータの読み込みが正常に行われません。姑息な手段ですが、以下のようなワークアラウンドを考えました。

  1. Excelから出力したTSV(Tab Separated Values)ファイルの、フィールドデータに含まれる改行を @@@ に置換
  2. InDesignでデータ結合を実行
  3. 全文検索により @@@ を改行に置換

というわけで、Excelから出力したTSVファイルを処理するPerlスクリプトを作りました。

エンコードの問題
ExcelからTSVファイルを出力するとき、「テキスト(タブ区切り)」を選択するとTSVファイルはShift-JISになります。人名や地名を含むデータの場合、Excel上では正しく表示されているのに、TSVファイルにすると文字化け(?で表示される)することがよくあります。そこで、ExcelからTSVファイルを保存するときに「Unicodeテキスト」を選択する運用としました(TSVファイルのエンコーディングUTF-16)。

InDesignのデータ結合で読み込めるユニコードUTF-16なので、Perlスクリプトから生成されるTSVファイルもUTF-16にしたかったのですがうまくいきません。仕方なく、PerlスクリプトからはUTF-8のTSVファイルを出力し、秀丸でBOM付きUTF-16に変換することにしました。ちょっと余計な手間が残りますが、何とか使えるようになりました。

【動作確認】
Windows7 x64
Perl v5.10.1
InDesign CS4

#!perl
#
# ======================================================
#  TSVファイルのフィールドに含まれる改行を@@@に置換する
# ======================================================
#
# [変更履歴]
#
# Ver1.00  2010/07/05 初版
# Ver2.00  2010/12/23 入力ファイルをShift-JIS CSVファイルから、Unicode TSVファイルに変更した。
#   Excel上での文字の表示にはUnicodeが使われるが、通常の方法でCSVファイルに保存するとShift-JIS
#   になるため、文字化けが発生することがある。
#
# [スクリプトの解説]
#
# InDesignのデータ結合でCSV/TSVファイルの流し込みをするとき、フィールドの中に
# 改行が含まれていると、正しく流し込みができない。この問題を解決するために、
# データ結合の前に、フィールドの中に含まれる改行を"@@@"に置換する。
# InDesignでデータ結合した後に、"@@@"を改行に置換することにより、フィールド
# の中の改行を再現できる。
#
# 変換後、***_OUT.TXT という名称のファイルに結果を出力する。
# 出力されるTSVファイルのエンコーディングはUTF-8で、InDesignが読めるのはUTF-16なので、
# 秀丸エディタでBOM付きのUTF-16に変換して保存する。
#
# [使用方法]
#
# コマンド・プロンプトより、以下の書式に従ってコマンドを入力する。
#
# 書式:
#  cr-u.exe -i <ソースのTSVファイル名称>
#
#  ファイル名 => *.txt
#
# 【 重 要 】
#
# ExcelからTSVファイルを保存するときに、Unicodeテキストを指定すること。
#
# 参考ページ:
#   http://www.din.or.jp/~ohzaki/perl.htm#CSVwithCRLF
#

use warnings;
use strict;

use utf8;
binmode STDOUT, ":encoding(shiftjis)";  # 標準出力をShift-JISにする
binmode STDERR, ":encoding(shiftjis)";  # 標準エラー出力をShift-JISにする
use Encode qw/encode decode from_to/;
use Getopt::Std;
use File::Basename;

print "*** TSVファイルの改行処理スクリプト Ver 2.00 ***\n\n";

# ― グローバル変数宣言 ――――――――――――――――――――――――――┐
our $opt_i = undef;             # -iオプションを取得するための変数

# ―――――――――――――――――――――――――――――――――――――┘

# ― 引数を取得する ――――――――――――――――――――――――――――┐

$opt_i = undef;
unless( getopts('i:')) {
  print "エラー:引数の取得に失敗しました。\n";
  &display_usage();
  die;
}

# -iオプションが指定されていない場合は、使い方を表示してスクリプトを終了する
if (!$opt_i) {
  &display_usage();
  exit;
}

# -iオプションで指定されたファイルが存在するかどうか確認する
my $input_fname = decode('shiftjis', $opt_i);   # ファイル名をShift-JISにデコードする
unless (-e $opt_i) {
  die "エラー:指定されたテキスト・ファイルがありません; ファイル名=$input_fname\n";
}

# ―――――――――――――――――――――――――――――――――――――┘

# ― 出力ファイルをオープンする ――――――――――――――――――――――┐

my $output_fname = basename($input_fname, ".txt");
$output_fname = $output_fname . "_OUT.txt";
#
# open OUTPUT_TSV_FILE, '>:encoding(utf16)', encode('shiftjis', $output_fname)
# ※ 出力ファイルをutf16にすると文字化けする
#
open OUTPUT_TSV_FILE, '>:encoding(utf8)', encode('shiftjis', $output_fname)
  or die "エラー:出力ファイルをオープンできません; ファイル名=$output_fname\n";

# ―――――――――――――――――――――――――――――――――――――┘

# ― 入力テキスト・ファイルをパースする ――――――――――――――――――┐

open INPUT_TSV_FILE, '<:encoding(utf16)', $opt_i
  or die "エラー:入力ファイルをオープンできません; ファイル名=$input_fname\n";
while (my $line = <INPUT_TSV_FILE>) {
  $line .= <INPUT_TSV_FILE> while ($line =~ tr/"// % 2 and !eof(INPUT_TSV_FILE));
  $line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/\t/;
  my @values = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_}
                ($line =~ /("[^"]*(?:""[^"]*)*"|[^\t]*)\t/g);

  # 各フィールドデータの中に改行が含まれていたら、"@@@"に置換する
  my $one_tsv_line = "";
  my $column_cnt = 0;
  foreach my $field (@values) {
    #print "$field\n";
    $field =~ s/\n/@@@/g;
    $one_tsv_line .= $field;
    if ($column_cnt < @values - 1) {
      $one_tsv_line .= "\t";
    }
    $column_cnt++;
  }
  print OUTPUT_TSV_FILE "$one_tsv_line\n";
}

print "\n$output_fname が作成されました\n\n";

close INPUT_TSV_FILE;
close OUTPUT_TSV_FILE;
exit;

# ―――――――――――――――――――――――――――――――――――――┘


################################################################################
# 処理概要: 本スクリプトの使い方を表示する
#
sub display_usage {
  my $msg = <<EOF;

[使用方法]

コマンド・プロンプトより、以下の書式に従ってコマンドを入力する。

書式:
  cr-u.exe -i <ソースのTSV名称>

 ファイル名 => *.txt

【 重 要 】

 ExcelからTSVファイルを保存するときに、Unicodeテキストを指定すること。

EOF
  print $msg;
}

【 免 責 】

上記スクリプトの使用により発生する、データの破損などのあらゆる不具合・不利益については、一切の責任を負いかねますのでご了解ください。

【 蛇 足 】

このスクリプトのテストをしていて気が付いたのですが、InDesignはデータ結合の際にフィールドデータに含まれるダブルクォートを破棄してしまうらしい。。。