初めてご質問させて頂きます。
VBを用いて初めてプログラムを作成するのですが、教えて頂きたい事がございます。
・レコード長が300バイトの固定長。
・ファイルレイアウトが決まっている。
・半角文字と全角文字が混在している。
以上のようなテキストファイルを読み込んで、
「レコード長が300バイトを超えている」とか、
「半角文字が入っていないといけないところに全角文字が入っている」とか、
「全角文字が入っていないといけないところに半角文字が入っている」というレコードをエラーとして、画面には「何行の何カラムがエラー」という風にメモ帳等に出力させたいと思っております。
テキストファイルの件数を読み込み、「○件の読み込みが終了しました。」と
表示させる所まではプログラムを作成できたのですが、
上記のような条件をいれるところで、どのようにプログラムを作成していけばいいのかが分からず、中断しております。
誠に恐縮ですが、プログラムの構築方法についての回答をどなたか宜しくお願い致します。
> VBを用いて初めてプログラムを作成するのですが
現行バージョンのVB(VB.NET 2003)を使用されているのでしょうか?
それとも、旧バージョン(VB.NET 2002、VB6、VB5…)でしょうか。
バージョンが分からないので、具体的なコードは示せませんが、
とりあえず一般論だけ。
> ・半角文字と全角文字が混在している。
文字コードは何でしょうか。UTF-8? UTF-16? Shift_JIS? EBCDIC?
それによっては文字コードの変換が必要になるかも知れません。
> 「レコード長が300バイトを超えている」とか、
ファイルサイズが300バイトの倍数かどうかで判定されては如何でしょう。
あるいは、レコード末端を示すデータ(例えば改行コードとか)があるなら、
それぞれの末端文字までのバイト数を数える事でも判定できますね。
> 「半角文字が入っていないといけないところに全角文字が入っている」とか、
VB.NETであれば、System.Text.RegularExpressions.Regexクラスを使うと良いかと。
> 上記のような条件をいれるところで、どのようにプログラムを作成していけばいいのかが分からず
ファイルをレコード単位で走査するようにし、各行のレコードを
If文で調査して行けば良いのでは。
回答の程、誠にありがとうございます。
使用しているVBはVisualBasic 6.0です。
また、半角文字と全角文字が混在しているのは、Shift_JISコードです。
文字コードの変換等が必要なのでしょうか?
私が今、考えているのはまず、ファイルの1レコード目を読み、レコード長をLen関数で聞き、戻り値が300バイトを超えていたら、その場でエラーとし次のレコードを読み、最後に’○行:レコード長不当’といった表示をさせる。
戻り値が300バイトを超えていなかったら、○カラムから○カラムまでは半角?or全角?といったチェックを1レコードずつ行なっていき、全件読み終えた後に’○行○カラム:エラー’といった表示をさせようと思っております。
その際の全角文字と半角文字の判別の仕方のロジックがよく分からなかったので、回答の程宜しくお願い致します。
> 文字コードの変換等が必要なのでしょうか?
VBの文字列は、Unicode(UTF-16)が利用されています。
この文字コードでは、どの文字も「2バイト」で表現されます。
(Shift_JISは、半角が1バイト、全角が2バイトですね)
この違いにより、バイト単位の文字列操作が必要な場合は、
StrConv関数(のvbFromUnicodeやvbUnicode定数)を使って
Unicode/Shift_JIS変換を行う事があります。
> レコード長をLen関数で聞き
それでは駄目だと思いますよ。
VB6のLen関数は、文字数を返します。バイト数ではありません。
なので、半角の「abc」や、全角の「あいう」を渡したとしても、
どちらも Len関数では 3 が返される事になります。
バイト数を返す LenB という関数もありますが、先述したように、
VB6では、すべての文字が2バイトで表現されるので、結果として、
「LenBの値 = Lenの値 * 2」にしかなりません。
なので、StrConv関数でShift_JISに戻してから LenB を使う事になります。
あるいは、String型を使わず、Byte型配列を使うという手もあります。
Openステートメントでランダムアクセスモードを選び、Get #ステートメントで
読み込むようにすれば、固定長データを効果的に読み込む事ができます。
幸い、Byte配列とStringには互換性があり、
Dim s As String, b() As Byte
s = "あいうえお123"
b = s
とすれば、配列変数bに、"あいうえお123"がUnicode形式で格納されますし、
逆に、Byte型の動的配列を、String型変数に代入することもできます。
# 下記サイトの 15番あたりが参考になるかも。
# http://www.interq.or.jp/www-user/komurak/progtec/
> 全角文字と半角文字の判別の仕方の
幾つかの方法がありますが、とりあえず簡単そうな物を。
案1: StrConv, Len, LenB関数を使って調べる。
「Unicode文字列でのLen関数の戻り値」と、
「Shift_JIS文字列でのLenB関数の戻り値」が
一致していたら、「すべて半角」とみなす方法です。
案2: Forループを使って1文字ずつ調べる。
文字の切り出しにはMid関数、文字の調査にはAsc関数を使います。
Asc関数で0〜255の範囲が返されれば半角、それ以外は全角という判定法です。
レコード長のエラー判定はStrConv関数でShift_JISに戻してから LenB を使う事によって解決致しました。
しかし、全角文字と半角文字の判別方法として、Forループを使って1文字ずつ調べ、Asc関数で0〜255の範囲が返されれば半角、それ以外は全角という判定法でしてみたのですが、うまくいきません。以下に私が組んだソースを載せますので、どこがいけないかご指摘の方お願い致します。
Dim i
Dim Msg, Style, Title, Response
Dim TextLine, temp
Dim objFileSystem As Object
Dim objFile As Object
Dim strFileName As String
Dim strTemp As String
Dim lngSourceLength As Long '渡された文字列の長さ
Dim strResult As String '変換後文字列を格納するバッファ
Dim strSingle As String '抜き出された 1 文字分のバッファ
Dim intCharCode As Integer '1 文字の文字コードを格納する
Dim m As Long 'ループカウンタ
Dim intLine1, intLine2, intLine3, intLine4, intLine5 As Integer 'エラーの行数を格納する
Dim intColumn1, intColumn2, intColumn3, intColumn4, intColumn5 As Integer 'エラーのカラム数を格納する
Dim intRecord As Integer 'レコード長エラーの行数を格納する
i = 0 ' 行数を初期化する。
m = 0 ' ループカウンタを初期化する。
Open UserForm1.txtFileMei.Text For Input As #1 ' ファイルを開きます。
Do While Not EOF(1) ' ファイルの終端までループを繰り返します。
Line Input #1, TextLine ' 行を変数に読み込みます。
temp = TextLine ' 行を変数に保存
Debug.Print TextLine ' イミディエイト ウィンドウに行を表示します。
strTemp = StrConv(temp, vbFromUnicode) ' 行全体内の全角文字 (2 バイト) を半角文字 (1 バイト) に変換します。
Debug.Print "LenB関数の結果:"; LenB(strTemp) ' 行のバイト数を返す。
i = i + 1 ' 行数に1を加える。
lngSourceLength = LenB(strTemp) '文字列の長さを得る
strResult = Space$(lngSourceLength) '変換後文字列を格納するバッファサイズを確保
strSingle = Space$(1) '1 文字分のバッファを確保
If lngSourceLength = 300 Then 'レコード長が300バイトなら・・・
For m = 1 To lngSourceLength Step 1 '文字列の最後までループ
Mid(strSingle, 1, 1) = Mid$(strTemp, m, 1) '1 文字だけ抜き出す
intCharCode = Asc(strSingle) 'Asc 関数で文字コードを得る
Select Case m
Case m >= 0 And m <= 51 ' 0〜51カラム以内で(半角領域)
If intCharCode >= 0 And intCharCode <= 255 Then ' 0〜255以内の範囲が返ってきたら半角
intLine1 = 0
Else ' 0〜255以外の範囲が返ってきたら全角
intLine1 = i ' エラーの行数を変数に保存
intColumn1 = m ' エラーのカラム数を変数に保存
Debug.Print "全角(半角)エラー:" & intLine1 & "行 " & intColumn1 & "カラム"
'イミディエイト ウィンドウに行とカラムを表示する"
End If
Case m >= 52 And m <= 75 ' 52〜75カラム以内で(全角領域)
If intCharCode >= 0 And intCharCode <= 255 Then
intLine2 = i ' エラーの行数を変数に保存
intColumn2 = m ' エラーのカラム数を変数に保存
Debug.Print "全角(半角)エラー:" & intLine2 & "行 " & intColumn2 & "カラム"
'イミディエイト ウィンドウに行とカラムを表示する"
End If
Case m >= 76 And m <= 85 ' 76〜85カラム以内で(半角領域)
If intCharCode >= 0 And intCharCode <= 255 Then
intLine3 = 0
Else
intLine3 = i ' エラーの行数を変数に保存
intColumn3 = m ' エラーのカラム数を変数に保存
Debug.Print "全角(半角)エラー:" & intLine3 & "行 " & intColumn3 & "カラム"
'イミディエイト ウィンドウに行とカラムを表示する"
End If
Case m >= 86 And m <= 105 ' 86〜105カラム以内で(全角領域)
If intCharCode >= 0 And intCharCode <= 255 Then
intLine4 = i ' エラーの行数を変数に保存
intColumn4 = m ' エラーのカラム数を変数に保存
Debug.Print "全角(半角)エラー:" & intLine4 & "行 " & intColumn4 & "カラム"
'イミディエイト ウィンドウに行とカラムを表示する"
End If
Case m >= 106 And m <= 300 ' 106〜300カラム以内で(半角領域)
If intCharCode >= 0 And intCharCode <= 255 Then
intLine5 = 0
Else
intLine5 = i ' エラーの行数を変数に保存
intColumn5 = m ' エラーのカラム数を変数に保存
Debug.Print "全角(半角)エラー:" & intLine5 & "行 " & intColumn5 & "カラム"
'イミディエイト ウィンドウに行とカラムを表示する"
End If
End Select
Next m
Else ' レコード長が300バイトでないなら・・・
Debug.Print "レコード長不当:" & i & "行" ' イミディエイト ウィンドウに行を表示する。
intRecord = i ' 行数を変数に保存
End If
Loop
頭から順番に取得した文字のコードが
129〜159または224〜252
であれば全角文字の1バイト目です。
(この場合必要なければ2バイト目を読み飛ばします)
それ以外の場合は半角文字です。
またStrConvでシステムの既定のコード ページにアクセスする
場合はLen,Mid,AscじゃなくLenB,MidB,AscBを使用します。
以上の点を考慮してプログラムを修正してみてください。
こんな感じ
Dim a As String
Dim i As Integer
a = "アイウあいう"
a = StrConv(a, vbFromUnicode)
For i = 1 To LenB(a)
Debug.Print Hex$(AscB(MidB$(a, i, 1)))
Next
>a = "アイウあいう"
アイウは半角カナだったため正しく表示されてません
a = "abcあいう"
でもいいです。
回答の程、ありがとうございました。
ご指摘の通り、上記のソースにて、
For m = 1 To lngSourceLength Step 1
Mid(strSingle, 1, 1) = Mid$(strTemp, m, 1)
intCharCode = Asc(strSingle)
のところを、
For m = 1 To LenB(strTemp)
MidB(strSingle, 1, 1) = MidB$(strTemp, m, 1)
intCharCode = AscB(strSingle)
として、また、intCharCodeの範囲の条件を129〜159または224〜252であれば全角文字の1バイト目、それ以外の場合は半角文字というようにプログラムを変更して、エラーとなるテキストファイルを読ませてみたのですが、
レコード長が300バイトを超えるレコードに関しては、
Debug.Print "レコード長不当:" & i & "行"
にて、イミディエイト ウィンドウに表示されたのですが、
半角領域に全角文字が混在していたり、全角領域に半角文字が混在していたりするレコードに関しては、上記のソース内のSelect Case構文の中の
Debug.Print "全角(半角)エラー:" & intLine1 & "行 " & intColumn1 & "カラム"
が、うまくイミディエイト ウィンドウに表示されませんでした。
考えられる原因としては他にないのでしょうか?
それとも、For関数やMidB関数やAscB関数の使用方法に誤りがあるのでしょうか?
単純な事かもしれませんが、どなたか回答の程宜しくお願い致します。
全角の場合は2バイト分カウンタを進めていますか?
カラム数のカウンタを変数mで表しており、For関数等で1バイトずつ、全角か半角かのチェックを行なっております。
各カラム数の範囲の条件式で、もし半角領域に全角文字が入っているエラーの場合は、「m = m + 1」という処理を加えました。
しかし、うまくウィンドウに表示されないのは、どうしてでしょうか?
なんとなく、上記のソース内のFor関数の中へと入っていないような気がするのですが・・・。
回答の程、宜しくお願い致します。
すっかり肝心なところを見落としてました
Select Case m というのは m = (caseに記述されている値)を判定します。
上記のソースだと下記のような判定がされるためどこにも入りません
m = (m >= 52 And m <= 75) (右辺はTrue=-1かFalse=0にしかなりません)
したがって文法的にはイマイチですが
If 判定式 Then
(処理)
ElseIf 判定式 then
(処理)
ElseIf
(処理)
End if
に書き直せばOKだと思います
例の場合だと
If m<=51 then
(処理)
ElseIf m<=75
(処理)
ElseIf m<=85
・
・
・
EndIf
という感じです
上とその上では
ElseIfの後のThenが抜けてるとこがありました。
的確なアドバイスどうもありがとうございました。
言われた通りにプログラムを修正したら、無事、
全角(半角)エラーチェックプログラムが動作しました。
誠にありがとうございました。
また次回、VisualBasicにて不明な点や分からない点等が出てきましたら、
ホームページ上で出題したいと思っておりますので、その時は宜しくお願い致します。
ツイート | ![]() |