プロが教える店舗&オフィスのセキュリティ対策術

master.csvの内容
11111,eeeeee,55555

for /f "delims=" %%A in (master.csv) do call :sub1 "%%A"
del master.csv
ren master6.csv master.csv
goto :eof

:sub1
set "LINE=%~1"
echo %LINE:,=2C%>>master6.csv

以上のようにカンマを2Cに変換し、改行コード(CR)(LF)を0D0Aに変換したいのですが、
どうしたらよいでしょうか?
また、同時に実行することは可能でしょうか?

A 回答 (5件)

>・ID_before"の値が "0001010114100DD800" をunhexして、CSVファイルに出力したものをバイナリーエディターで見ると"01010114100DD85C30"となってしまうのです。



自分の環境では、まったく同じにはなりませんでしたが、確かに似たような状態になりますね。
(バイナリ値の前後に \0 が付くんですね)

で、正直に申し上げて、これをどうやって回避するかは、自分にはわかりかねます。
何度も言うように、バイナリをこの方法でやり取りするところに無理があるのであって。

CSVにバイナリを入れる事自体がイレギュラーなので、繰り返しになりますが、私なら最初からこうします。
(前の回答の繰り返しです)

SQL文は以下でCSVを作成
SELECT ID_before,CODE_AFTER,NAME_AFTER FROM work1 into outfile "C:/master2.csv" fields terminated by ",";

CSVをバイナリだけ除いて移行先のテーブルにインポート

で、多分「それじゃ移行先でバイナリが入れられないじゃないか」と思われるでしょうが、「だから!」そこは移行した先でバイナリにするんですって!

(例) UPDATE ikou SET ID_after = unhex(ID_before) WHERE ID_after IS NULL;

今、自分の環境でサクッと試してみましたが、何の苦労もなく入れられました。

現在、実装されていると構成が変わるので、手戻りにはなりますが、「CSVにバイナリを入れる」事に拘泥して、この後わんさか出てくる諸問題を考えると、質問者様も上司も顧客もオールハッピーになるには、これしかありません!

まあ、ひょっとしたら回避方法を知っている識者がMySQLカテにいるかもしれませんけどね。
そこは、またあちらで聞いてみてはどうでしょうか?


>・CSVに出力したものをバイナリーエディターでみると、改行コードが、CrLF  にしたいのですが、LFだけしか表示されません。

こちらは私の環境でもそうなります。
これは mysql.exe の仕様っぽいですね。

ただ、移行先でLFのままでも問題がなければ、それでもOKと思いますけど。

移行先の仕様上、または要件定義上、CRLF以外は許容できないのであれば前の回答で提示したVBScriptを改造して、「LF→CRLF」にファイル丸ごと置換するようなものを作って間に噛ませてやればよいと思います。
(そこはご自身でお願いしますね)


>・CSVに出力したものをバイナリーエディターでみると、最後の行に’1A’とバイナリーエディターで表示されてしまうのです。

ああ、失礼。そのSUB(0x1A)ですね。

こちらは、私の環境では再現できなかったですが、想像は付きます。

多分結果のCSVに、COPYコマンドでヘッダー行を付けているのではないですか?
その場合は、末尾に0x1Aが付くのはMS-DOS時代からの仕様です。
(ハイ、そこのオジサン連中はニヤニヤしない!w)

copy Aファイル + Bファイル 最終ファイル

ではなく

copy /b Aファイル + Bファイル 最終ファイル

とすれば、末尾の0x1Aは消えます。

この回答への補足

UPDATE ikou SET ID_after = unhex(ID_before) WHERE ID_after IS NULL;
を行うと、一度うまくいあったのですが、SQLを投げる時に、character はしないほうがよいのですかね?

ECHO SET character SET sjis;>create0_sql.sql

ちなみにUPDATE して格納するテーブルの定義は型は関係ありますか?

補足日時:2010/12/06 17:59
    • good
    • 0
この回答へのお礼

utakataXEXさん ありがとうございます。

お礼日時:2010/12/06 17:56

ささっと要点だけ。



> UPDATE ikou SET ID_after = unhex(ID_before) WHERE ID_after IS NULL;
> を行うと、一度うまくいあったのですが、SQLを投げる時に、character はしないほうがよいのですかね?

文字コード関連のSET文は、今回の場合に関しては関係ないです。
日本語の項目があって、それを表示したいならSETすればよいし、英数字しか扱わないのであれば、SETする必要無し。

> ちなみにUPDATE して格納するテーブルの定義は型は関係ありますか?

これは関係ありますよ。
バイナリを入れるのだから、それ用の型で定義する必要があります。

因みに私が、この質問に答えるのに作成したテーブルは以下の通りです。
(主キーやリレーションは一切考慮していません)

create table work1
(
ID_before varchar(18),
CODE_AFTER varchar(10),
NAME_AFTER varchar(50)
);

create table ikou
(
ID_before varchar(18),
ID_after varbinary(9),
update_date timestamp
);

今回のような小さいバイナリを入れるのであれば、VARBINARYを使用します。
定義する場合は、上記のように VARBINARY(n) で定義するか、VARCHAR(n) CHARACTER SET binary で定義します。

※IDmの長さは原則的に不変なはずなので、BINARY(n)=CHAR(n) CHARACTER SET binary の方が適当だったかな?
    • good
    • 0
この回答へのお礼

utakataXEX さん ありがとうございます。

お礼日時:2010/12/08 09:13

#1、2 です。



正直、今回の#2の補足質問の内容は、私には理解できません。

>"ID_before"の値が "0001010114100DD800" をunhexすると、"01010114100DD85C30"

work1.ID_beforeに入っているのは文字列ですか?バイナリですか?
文字列で 0001010114100DD800 になっているなら、unhexしたら文字列"01010114100DD85C30"にはなり得えません。
(人間には読めない値になります)

これがFelicaのIDmだとしたら、何故(Felicaチップにアクセスする時以外にも)バイナリ値でやり取りしないといけないのかが、相変わらずわからないのですが、ええ、そこはいいです。もう私が折れましょうw

しかし、折れるに折れられない部分があります。他の回答でも書いた事ですが、

「何で、unhexしたバイナリ値をCSVに入れるのかなぁ?」

バイナリに何が入るかは不定ですよね?
たまたまカンマ(と同じASCIIコード)や Cr(と同じASCIIコード)や Lf(と同じASCIIコード)が入っていたらどうなります?
その事の可否はフェリカネットワークスやソニーが決める事であって、私達使用する側ではどうあっても制御できませんよね。
「たまたまお客様のIDmには制御文字が含まれておりますので、取り込めませんでした。お諦めください」
って言いますか?

「何故バイナリの先頭と末尾が変わったか?」について、はっきりと特定はできませんが、バイナリとテキストが混在したCSVからバイナリだけ抜いて、移行先に上手い事、格納するなんて、普通の mysql.exe や他のクライアントツールではそもそもが無理ですよ。
うまく行く事もあるでしょうが、うまく行かなかった時にリカバリーする方法がありません。
どうしても、それが必要なら C言語 か何かでクライアントツールを作るしかありません。

で、とにかく私がお薦めする、ソリューションとしてはですね。繰り返しになりますが。

「CSVにバイナリを入れるのは諦める」

これしかありません。

バイナリを扱いたい、と言うのは、ええ、くどいけど折れますよ。
でもCSV渡しで、確実にバイナリ値を移行したいなら、HEXした文字列で渡すしかありませんよ、普通。

【移行元DB】
IDmがHEX表記の文字列で格納されている(で合ってますよね?)

CSVに『unhexせず』そのまま格納(ここが重要!)

【移行先DB】
IDmは文字列とバイナリ両方のカラムを持っている(でいいのかな?)

文字列のカラムはそのままインポートなりインサートなり、すればよい

バイナリのカラムの方は(ここで初めて)unhexしたものを入れればよい
例えば
update kekka_table set idm_bin = unhex(idm_mojiretu) where idm_bin is null
とかね。

あ、これが20バイト足らずなので、上記のように書いていますが、バイナリの大きさが数MB~GBクラスになったら、さすがにそんな事はしませんよ。
その場合は、LOB(ラージバイナリオブジェクト)になるので、そもそも扱いが違います。

>また、改行コードが、CrLF  にしたいのですが、LFだけしか認識しません。

どこの改行コード?認識しないとは誰(何)が?

>最後にSUBが入ってしまいます。

何の最後に?CSVの最後に"SUB"と言う文字列が入る、と言う事ですか?

最後はちょっと苦言。

>確認したところ、上記の変換はしなくてよかったのですが、

むむむ。
「本当にこれで要件満たせるの?」と念押しした上での#2の回答だったんですけど。。。

失礼を承知で言ってしまいますが、少し「泥縄」と言うか「後手後手」過ぎませんか?

#1 で書いた、「わざわざ物凄く遠回りなシステム構成/実装」と感じる部分が、質問者様ご自身に起因するものなのか、上司やお客さんの無茶ぶりによるものなのか、私には知る由もありませんが、多分、このままだと長大な質問ツリーがシステムリリースまで(リリース後の運用フェーズでも)続くような気がします。

確実に言える事は、バイナリやキャラクタセットについての扱いを質問者様ご自身があまり理解されていないようで、そのために質問や、実装方法も、なんとなくトンチンカンなものになってしまっているようです。

ともあれ、乗りかかった船。もうここまで来たら、できる限りの回答はしますので、補足をお願いします。
(私自身の業務に支障を来たさない範囲でですがwww)

この回答への補足

・ID_before"の値が "0001010114100DD800" をunhexして、CSVファイルに出力したものをバイナリーエディターで見ると"01010114100DD85C30"となってしまうのです。
・CSVに出力したものをバイナリーエディターでみると、改行コードが、CrLF  にしたいのですが、LFだけしか表示されません。
・CSVに出力したものをバイナリーエディターでみると、最後の行に’1A’とバイナリーエディターで表示されてしまうのです。

補足日時:2010/12/05 09:54
    • good
    • 0
この回答へのお礼

utakataXEX さん 有難うございます。

お礼日時:2010/12/05 09:45

#1 です。



> インストールできない環境なので、
> BATからVBScriptで処理するには、どうしたらよいのでしょうか?

この補足があると言う事は、やりたい事はこれで間違いない、と言う事ですね。
(何か釈然としないけど、まあ、そこまで私がどうこう言える立場でもないので。。。)

実装に手間のかからない方法で書きます。
パスやターゲットの文字列等は引数にして汎用的に作る事も可能ですが、固定にします。
(自分が面倒臭いだけw)

まず、以下のVBScriptを replace_crlf.vbs として保存してください。

ここから↓↓↓↓

Const ForReading = 1, ForWriting = 2, ForAppending = 8

Dim vbCrLF
Dim fso, f
Dim ifile
Dim strBuf

' 改行変数
vbCrLF = chr(13) & chr(10)

' ファイル名
ifile = "master.csv"

' ファイルオープン
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(ifile, ForReading)

' ファイルの内容をReadAllで変数に格納
strBuf = f.ReadAll()

' ファイルクローズ
f.Close

' カンマを"2C"に置換+CRLFを"0D0A"に置換(ファイル丸ごと)
strBuf = Replace(strBuf, ",", "2C")
strBuf = Replace(strBuf, vbCrLF, "0D0A")

' 結果表示
WScript.Echo strBuf

ここまで↑↑↑↑

ReadAll で丸ごと取得した変数を丸ごと2回置換し、標準出力に一気に吐き出す、と言う超手抜き版です。
CSVの件数がかなり多い(または横列が長大の)場合は結構時間がかかります。

(1)エラー等の取り回しを考えても、Readline で1行ずつ処理した方がいいでしょう。
(2)結果としては改行の無い長大な1行データになりますが、この構成で横列が何バイトまで出力可能なのかは、私にもわかりません。
Echoのリダイレクトではなく、出力ファイルを定義して改行無しのWriteで追記していく方が確実です。
(3)最初に書いたとおり汎用的なパラメータ取得にはしていません。

上記3点はご自身の自助努力でお願いします。

上記VBSを使って、前の回答で使ったバッチを少し弄って、こんな感じにします。

@echo off

REM FOR文使わず楽ちんバージョン(VBS版)

REM カンマを"2C"に置換+CRLFを"0D0A"に置換(ファイル丸ごと)
call cscript //nologo replace_crlf.vbs > master6.csv

del master.csv
ren master6.csv master.csv


それと、重要な事を書き忘れましたが、今書かれているバッチは、パスを意識した作りになっていませんよね。
これも老婆心から補足しておきます。

本番への適用が初回の1回~数回だけの移行処理で、実行も手動でするなら、このままで問題ありません。

しかし、もし、タスクスケジューラで毎日定期的に起動するのであれば、このままでは実行時にエラーになるので、使用するコマンド(DOSコマンドは除外)やファイル名はフルパスで記述するようにしてください。
(システムパスが通っている場合でもそうするのがセオリーです)

これは、Unix/Linuxなどで cron で処理する場合も同様です。

でも。。。やっぱり構成や基本設計から見直した方がいいんじゃないかなぁ。
ここの識者の皆さんも(わざわざ言わないだけで)同意見だと思うけど。

あの(CSVではない)CSVファイルをどうやって運用していくつもりなのか、気になる。。。

この回答への補足

何度もすみません。
確認したところ、上記の変換はしなくてよかったのですが、
以下のSQLを発行してCSVに出力するのですが、
DBに格納されている"ID_before"の値が "0001010114100DD800" をunhexすると、"01010114100DD85C30"
と変換されてしまうのですが、どうしてでしょうか?
また、改行コードが、CrLF  にしたいのですが、LFだけしか認識しません。
あた、最後にSUBが入ってしまいます。
どうしたら、よいでしょうか?

SELECT unhex(ID_before),CODE_AFTER,NAME_AFTER FROM work1 into outfile "C:/master2.csv" fields terminated by ",";>>create3_sql.sql

補足日時:2010/12/04 21:39
    • good
    • 0
この回答へのお礼

utakataXEX さん ありがとうございます。

お礼日時:2010/12/04 21:29

まず、ご質問の回答としては。



Windows(DOS)のコマンドで、カンマは'2C'に置換できます。
(既に質問者様ご自身で実装されている通りです)

しかし、改行(CRLF)は置換できません。

制御文字のすべてがダメかと言うと、微妙にそうでもないんですが、改行については、どうやっても無理です。
findstrの正規表現にも改行を捕捉する術はありません。

VBScript や Windows PowerShell で Replace するとか、jperl や tr(Unixの置換コマンドのDOS移植版)などで、正規表現で改行を捕捉して置換するとかになります。

VBScriptなら何もインストールする必要がなくて済みますが、データが大量だと結構時間がかかるので、個人的には jperl や tr の使用をお薦めします。
(新規にユーティリティ類をインストール可能なら)

jperl を使った例で、質問者様のバッチを少し弄ると以下のようになります。

@echo off

for /f "delims=" %%A in (master.csv) do call :sub1 "%%A"

REM 改行(CRLF)を文字列"0D0A"に置換(ファイル丸ごと)
jperl -pe "s/\n/0D0A/g" < master6.csv > master6_tr.csv

REM ※リネームファイルを変更した点に注意!

del master.csv
del master6.csv
ren master6_tr.csv master.csv
goto :eof

:sub1
set "LINE=%~1"
echo %LINE:,=2C%>>master6.csv

:eof

これでご希望の事はできるのですが、こうなってしまうと、むしろ FOR文を使わず、jperlだけを使った方が楽ちんで速い、と言う事になります。
以下を見てわかるとおり、バッチ自体も非常にシンプルになります。

@echo off

REM FOR文使わず楽ちんバージョン

REM カンマを"2C"に置換+CRLFを"0D0A"に置換(ファイル丸ごと)
jperl -pe "s/,/2C/g" < master.csv | jperl -pe "s/\n/0D0A/g" > master6.csv

del master.csv
ren master6.csv master.csv


さて、回答としては以上なのですが、また少しおせっかいな事を書きます。

これで業務要件としては満たせますか?
CSVのカンマと改行だけ"2C"と"0D0A"に置換する意味がわからないのです。

因みに、例えば、2行あったら結果は以下のようになります。

1111,aaaaa,あ
2222,bbbbb,い

11112Caaaaa2Cあ0D0A22222Cbbbbb2Cい0D0A

この master.csv を受け取る側は、この内容で何ができるんでしょうか?
既にCSVではなくなっているし、改行も無いので通常のWindowsやLinuxのアプリケーションではあまり使わないフォーマットと言えます。
(しいて言えば、汎用機?それにしたって無理がある)

すべてのカラムがHEXになっているなら、まだ何となくわかりますけど。
(いや、何となくですけど)

何かこの、やり取りさせていただいても、「普通なら単純にできる事を、わざわざ物凄く遠回りなシステム構成/実装にしているのではないか?」と受け取れる内容なんですよ。

まあ、「これこそがやりたい事だ」と言われれば大きなお世話なんですが。。。

この回答への補足

インストールできない環境なので、
BATからVBScriptで処理するには、どうしたらよいのでしょうか?

補足日時:2010/12/04 08:07
    • good
    • 0
この回答へのお礼

utakataXEX さん 有難うございます。

お礼日時:2010/12/04 08:07

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!