|
■No64968 (コンバート後に悩む人 さん) に返信 > 全角文字も混在するので、上記の例では
VB6 同様、文字列は Unicode で管理されるため、 先の構造体は、『文字数』ベースで管理されることになります。
VB6 で LSet した場合も、あくまでも「文字数単位」で 複写されますよね。それと同じことです。
'----------------- '------ VB6 ------ Type typAAAA aaaa As String * 2 '2文字(≠2バイト) BBBB As String * 3 '3文字(≠3バイト) cccc As String * 5 '5文字(≠5バイト) dddd As String * 7 '7文字(≠7バイト) End Type
Type typBBBB strB As String * 100 End Type
Sub Main() Dim aaaa As typAAAA Dim BBBB As typBBBB
aaaa.aaaa = "あい" '2文字(≠2バイト) aaaa.BBBB = "xYz" '3文字(≠3バイト) aaaa.cccc = "VB6.0" '5文字(≠5バイト) aaaa.dddd = "[13579]" '7文字(≠7バイト)
'先頭17文字(≠17バイト)が置き換わる LSet BBBB = aaaa
'どこが NULL 文字か分かるよう、@に置き換えて表示 Debug.Print Replace(BBBB.strB, vbNullChar, "@") End Sub
'-------------------- '------ VB2005 ------ ' 構造体定義は No64967 を参照
Dim a1 As typAAAA Dim b1 As typBBBB
a1.aaaa = "文字" '2文字 a1.bbbb = "VB6" '3文字 a1.cccc = "[A&Z]" '5文字 a1.dddd = "1234567" '7文字
Dim size As Integer = Math.Max( _ Marshal.SizeOf(GetType(typAAAA)), _ Marshal.SizeOf(GetType(typBBBB)))
Dim bin(size - 1) As Byte With GCHandle.Alloc(bin, GCHandleType.Pinned) Dim pBin As IntPtr = .AddrOfPinnedObject() Marshal.StructureToPtr(a1, pBin, False) b1 = DirectCast(Marshal.PtrToStructure(pBin, GetType(typBBBB)), typBBBB) .Free() End With
'どこが NULL 文字か分かるよう、@に置き換えて表示 Console.WriteLine(Replace(b1.strB, vbNullChar, "@"))
Dim a2 As typAAAA Dim b2 As typBBBB
'100 文字分セットしておくことを忘れずに b2.strB = Strings.Left("漢字&半角を含むDATAです。" & StrDup(100, vbNullChar), 100)
With GCHandle.Alloc(bin, GCHandleType.Pinned) Dim pBin As IntPtr = .AddrOfPinnedObject() Marshal.StructureToPtr(b2, pBin, False) a2 = DirectCast(Marshal.PtrToStructure(pBin, GetType(typAAAA)), typAAAA) .Free() End With
Console.WriteLine(Replace(a2.aaaa, vbNullChar, "@")) Console.WriteLine(Replace(a2.bbbb, vbNullChar, "@")) Console.WriteLine(Replace(a2.cccc, vbNullChar, "@")) Console.WriteLine(Replace(a2.dddd, vbNullChar, "@")) '------------
> 「埋め込まれた配列インスタンスがレイアウトで宣言された長さと一致しないため、型をマーシャリングできませんでした。」 低レベルのコピー命令を使う以上、データの長さは常に意識しておかねばなりません。
構造体をコピーしたいけれど、文字数単位では無く、Shift_JIS 相当の バイナリ幅で処理して欲しい…というのであれば、各フィールドを Char 配列ではなく、Byte 配列で管理する必要があります。
そのかわり、入出力操作のたびに、Encoding 変換の手間が かかってしまうので、操作としては面倒になってしまいます。 (VB6 でいえば、StrConv で Unicode/ANSI 変換を毎回行うイメージです)
これが VB2008 であれば、String と Byte() を変換するための 拡張メソッドを作ることで、だいぶ楽にできるのですけれどね…。
> ここまでで思ったのが、VB6.0からのソースにとらわれずに > StringBuilderでやるしか方法がないのでしょうか。
それはどうでしょう。方法自体は、他にもいろいろあると思いますよ。
手間を惜しまないのであれば、固定長文字列型に相当するクラスや属性を 自作するという手法もあるでしょう。その固定長型で、CType 演算子なども オーバーロードさせておけば、VB6 の動作に、かなり近い物を 用意できるかも知れません。工数に見合うかどうかは別ですが。
まぁ、最終的にどの方法を選ぶかは、御自身の判断ということで。
とはいえ、StringBuilder だと「文字数」での管理になってしまいます。 もしも Shift_JIS バイナリとしてのバイト位置で管理したいなら、 MemoryStream や Byte() などで管理したほうが都合が良いかもしれません。
Dim sjis As Encoding = Encoding.GetEncoding(932)
Dim stm As New MemoryStream()
Dim w As New StreamWriter(stm, sjis) 'w.Write("答") '2バイト 'w.Write("Q&A") '3バイト 'w.Write("質問?") '5バイト 'w.Write("Answer!") '7バイト w.Write("答Q&A質問?Answer!")
Dim bin() As Byte w.Flush() stm.Position = 0 With New BinaryReader(stm) bin = .ReadBytes(stm.Length) End With
'この切り出し位置の一覧を「Dictionary(Of 位置情報)」などで '管理しておくと、多少は管理しやすくなるかも。 Console.WriteLine(sjis.GetString(bin, 0, 2)) Console.WriteLine(sjis.GetString(bin, 2, 3)) Console.WriteLine(sjis.GetString(bin, 5, 5)) Console.WriteLine(sjis.GetString(bin, 10, 7))
|