市販の見積作成ソフトを自社開発の見積作成ソフトに更改する案件でのこと。
データベースをコンバートしたら、コンバート元のデータベースで変なデータの持ち方をしている項目がありました。
見積書内容のデータベースにおいて、以下のような感じ。
DataId | 納期 |
---|---|
1 | 別途ご協議の上決定 |
2 | 令和_##年_##月_##日迄;6;5;20 |
3 | 受注後_&&&&&&日以内;90 |
4 | 受注後_&&&&&日以内;5 |
やりたいこと
新しいデータベースでは以下のようなデータの持ち方をしたい。
DataId | 納期 |
---|---|
1 | 別途ご協議の上決定 |
2 | 令和6年5月20日迄 |
3 | 受注後90日以内 |
4 | 受注後5日以内 |
元データでは数字部分が「_&&」もしくは「_##」に置き換えられ、数字部分は「;」区切りで文字列の最後尾にデータ保持をしている。
ただし「&」の個数はデータによってばらつきがある。
また「_&」「_#」の違いはよく分からない(分からなくても問題なし)
こんな感じのデータベースをVB.Netにて作成しているコンバートシステムにて文字列操作しながらコンバートします。
以下のコードで実現しました
とりあえずReplace関数を使うイメージはあったのですが、問題は「令和_##年_##月_##日迄;6;5;20」のようなデータ。「_##」がデータ中に3回登場し、それぞれ1回目は「6」、2回目は「5」3回目は「20」に書き換えなければなりません。
これをどうやって実現しようか少し悩みましたが、以下のコードでうまくいきました。若干泥臭いやり方です。
なお私の言語環境はVB.Netですが、おそらくVBAでも同様の考え方でいけるのではないかと思います。
'O_wRowData:コンバート元データベース
'N_wRowData:コンバート先データベース
Dim w(4) As String
Dim w1 As String
Dim r As Integer
If InStr(O_wRowData("納期"), ";") > 0 Then
w = Split(O_wRowData("納期"), ";")
w1 = w(0)
For r = 1 To w.Length - 1
If w(r) <> "" Then
If InStr(w(0), "&") > 0 Then
w1 = Replace(w1, "_&", w(r), 1, r)
End If
If InStr(w(0), "#") > 0 Then
w1 = Replace(w1, "_#", w(r), 1, r)
End If
End If
Next
Else
w1 = O_wRowData("納期")
End If
w1 = Replace(w1, "&", "")
w1 = Replace(w1, "#", "")
N_wRowData("納期") = w1
コード解説
①旧データを「;」でSplit
If InStr(O_wRowData("納期"), ";") > 0 Then
w = Split(O_wRowData("納期"), ";")
w1 = w(0)
~省略~
Else
w1 = O_wRowData("納期")
End If
旧データに「;」が存在する場合(Instr)、「;」でSplitし、一次元配列として宣言している変数wに格納。
「令和_##年_##月_##日迄;6;5;20」の場合、この時点で以下のように格納されています。
'w(0):令和_##年_##月_##日迄
'w(1):6
'w(2):5
'w(3):20
string型の変数w1に、データ本体のw(0)を持たせておきます。
旧データの「;」が存在しない場合(Ellse以下)、w1には旧データをそのまま格納しておきます。
②一次元配列wの要素数分ループ処理にて置換していく
For r = 1 To w.Length - 1
If w(r) <> "" Then
If InStr(w(0), "&") > 0 Then
w1 = Replace(w1, "_&", w(r), 1, 1)
End If
If InStr(w(0), "#") > 0 Then
w1 = Replace(w1, "_#", w(r), 1, 1)
End If
End If
Next
一次元配列wの要素数分(length)、ループ処理を行う。
まずは「_&」の置換から。「Replace(w1, “_&”, w(r), 1, 1)」が今回のミソです。
Replace関数の記述は以下の通り。
Replace(expression, find, replace[, start[, count[, compare]]])
それぞれの引数の説明は以下の通り。
引数 | 定数(値) | 説明 |
---|---|---|
expression | - | 置換元の文字列 |
find | - | 検索する文字列 |
replace | - | 置換する文字列 |
start | - | 検索開始位置を指定 省略した場合、1とみなす |
count | - | 検索する回数を指定 省略した場合、すべて置換 |
compare | vbBinaryCompare(0) | バイナリモードで比較 大文字と小文字を区別する |
vbBinaryCompare(1) | テキストモードで比較 大文字と小文字を区別しない |
Replace(w1, “_&”, w(r))だとシンプルで、変数w1の文字列から「_&」をすべて変数w(r)の文字列に置換します。
しかし今回は前から順番に置換していきたいので、Replace(w1, “_&”, w(r), 1, 1)と4つ目、5つ目の引数に数値を渡します。
こうすることで文字列を頭から検索し、1回のみ置換するように指定されます。
一次元配列wの要素数分(length)、ループ処理を行うことで順番に置換されていきます。
同様のことを「_#」においても行う。ループ中の変数w1の結果は以下のような遷移をたどるはず。
'r=1のときのループ処理後の結果
'w1=令和6#年_##月_##日迄
'r=2のときのループ処理後の結果
'w1=令和6#年5#月_##日迄
'r=3のときのループ処理後の結果
'w1=令和6#年5#月20#日迄
③②で置換されていない「&」「#」を削除し、新DBに格納
w1 = Replace(w1, "&", "")
w1 = Replace(w1, "#", "")
N_wRowData("納期") = w1
②では「_&」「_#」で検索してReplaceしましたが、実際のデータは「_&&&&&日後」など記号が複数並ぶため、不要な「&」「#」をReplaceで削除します。
綺麗なデータになった変数w1を新DBに格納して完了。
以上です。
Replace関数はしょっちゅう使用しますが4つ目、5つ目の引数を使うのは意外と初めてだったので新たな発見でした。
参考サイト
以下のサイトを参考にさせていただいています。ありがとうございます。