- PR -

null区切りされた文字配列について

投稿者投稿内容
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2005-08-30 14:19
引用:

Hongliangさんの書き込み (2005-08-30 00:02) より:

アンマネージドとの相互運用においてStringBuilderを使用すると、アンマネージドから文字を受け取るときに自動的にNULL文字を検索しそこまでしか格納しません。
ですからこの場合においては使用できません。



フォローありがとうございます。

これは知りませんでした。
一つ知識が増えました。
ありがとうございます。

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-30 14:42
引用:

Jubeiさんの書き込み (2005-08-30 14:17) より:
引用:

コード:

Dim str As String = Encoding.GetEncoding("SHIFT_JIS").GetString(bytArray)
TextBox1.AppendText(str)
    ★nullデータを代入しないと「日本語」と表示されるが、
     nullデータを代入すると先頭がnullの為、何も表示されない。





私のコードではSplit使ってnull文字を無くしているんですけど。。




すみません。
EncodeしてString型に代入した時点(Splitする前)で、
0ターミネイトされていた(null文字以降は切り捨てられていた)為、
Splitは、記載していませんでした。


[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 14:44 ]
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-30 16:25
とても参考になる情報なのですが、いまいち良くわかっていません。
度々、ヒントを頂いて申し訳ありませんが、もし良ければ再度レスをお願いします。

引用:

--------------------------------------------------------------------
アンマネージドとの相互運用においてStringBuilderを使用すると、アンマネージドから文字を受け取るときに自動的にNULL文字を検索しそこまでしか格納しません。
ですからこの場合においては使用できません。

そうなると後は使えそうなのが、Byte配列と、Stringです。
Byte配列については、まあ今までに十分出てますよね。



★ここまでは理解しています。
StringBuilderやString型は、格納する時点でNullターミネイトされて
しまうので、null区切りされている文字配列データの取得には使用できない
と思っています。
その為、同じ1byteのBYTE型配列で取得し、文字コードをSJIS(C++)から
Unicode(VB.NET)へ変換し、出力していました。

--------------------------------------------------------------------
↓↓↓以下の様な回答を頂きましたが、ここからが理解できません。


引用:

さてStringです。
Stringは変更不可でP/Inokeで値を受け取ることはできない、その場合はStringBuilderを使え、と言うのが定説だと思います。



★いろいろなホームページを見ましたが、皆StringBuilderを使用していました。

引用:

ですが、VB.NETのDeclare構文においては、ByVal/As Stringで宣言した場合、内部ではByRef扱いになります。そして同時にMarshalAs属性がUnmanagedType.VBByRefStrで宣言されている状態になります。
この状態では、普通に文字列を渡すことができ、そして返値として文字列を受け取ることが可能になります。
ただし受け取る場合は、事前に受け取る文字数分以上、何らかの文字で埋めておかなければ、余った分は切り捨てられます。受け取る文字数より多く文字を埋めていた場合、その文字は残ります。
//ちなみにDllImport属性でもref/ByRefで宣言しUnmanagedType.VBByRefStrを追加すれば同じ動作になります。



★この情報はとても参考になりました。
ありがとうございます。

引用:

問題なのは文字コードのマーシャリングでしょう。
Declare宣言で何故かみんな自動的につけてる(ようなイメージがある)Auto句ですが、これには歴とした意味がありますし漫然とつけて良いものではありません。
Unicode版の関数が扱える環境において、呼び出す関数をUnicode版の関数にし、関数とUnicode文字列でやりとりするという作用があります。
つまりNT系においては、関数にはUnicodeでエンコードされたバイト列が渡され、関数から受け取ったバイト列は自動的にUnicodeとして解釈されデコードされると言うことです。



★これは、VB.NETで使用する文字コードは全てUnicodeであると言う事を
伝えたい文章だと思っています。間違っていますか?

引用:

ではプラットフォームに関わらずAnsiとして文字列をやりとりしたい場合はどうするか。
……まあそのままですね。



★ここが知りたいです。
今回、Null区切りされた文字配列データ(SJIS)をアンマネージコードより
取得したいと思っています。
しかし、文字コードをマーシャリングしてString型に格納すると0ターミネイト
(null以降のデータは切り捨て)されてしまうので、どのような方法で取得が可能
あるか?

ちなみに、現在の作成中のコードを以下に記載致します。

--------------------------------------------------------------------
<DLLインターフェース>
TEST_RESULT TEST_APICALL TEST_StrGet(
 //IN :ディレクトリ名 
 char[10] DirName,

 //OUT :0ターミネートしたファイル名リスト(ASCIZ文字列:SJIS)
 char[100] FileList
);

<作成中のプログラムコード>
Public Declare ansi Function TEST_StrGet Lib "test.dll" ( _
 ByVal DirName As String, _
<MarshalAs(UnmanagedType.LPArray, _
ArraySubType:=UnmanagedType.LPStr, _
SizeConst:=100)> ByVal FileList() As String
) As Integer

Public Function TEST( _
 ByVal strDirName As String, _
 ByVal strFileList() As String, _
) As Integer

 Dim i, rc As Integer
 Dim DirName As String = "C:\"
 Dim FileList(100) As String

 rc = TEST_StrGet(DirName, FileList)
strFileList = FileList
 ★「FileList」に何も返却されない
 
  ・
  ・
  ・

End Function


[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 16:27 ]

[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 16:29 ]

[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 16:33 ]
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2005-08-30 17:05
諸農です。

引用:

taaさんの書き込み (2005-08-30 14:42) より:

EncodeしてString型に代入した時点(Splitする前)で、
0ターミネイトされていた(null文字以降は切り捨てられていた)為、
Splitは、記載していませんでした。



ん??本当にそうなったのですか?

次のコードで試してみましたが、
結果として、

空白行
日本語
日本語日本語

の結果を得ることが出来ましたよ。

コード:
Imports System
Imports System.Text

Class NullStrTest
  Shared Sub Main()
      Dim bytArray(20) As Byte
      bytArray(0) = &H0 
      bytArray(1) = &H93 
      bytArray(2) = &HFA 
      bytArray(3) = &H96 
      bytArray(4) = &H7B 
      bytArray(5) = &H8C 
      bytArray(6) = &HEA 
      bytArray(7) = &H0 
      bytArray(8) = &H93 
      bytArray(9) = &HFA 
      bytArray(10) = &H96 
      bytArray(11) = &H7B 
      bytArray(12) = &H8C 
      bytArray(13) = &HEA 
      bytArray(14) = &H93 
      bytArray(15) = &HFA 
      bytArray(16) = &H96 
      bytArray(17) = &H7B 
      bytArray(18) = &H8C 
      bytArray(19) = &HEA 
      Dim str As String = Encoding.GetEncoding("SHIFT_JIS").GetString(bytArray)
      Dim strs() As String = str.Split(Char.MinValue)
      Dim s As String
      For Each s in strs
          Console.WriteLine(s)
      Next s
  End Sub
End Class





もしかして求めているものが違うのかな?

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-08-30 17:50
鮮やかにスルーされたかと思いました 

引用:

StringBuilderやString型は、格納する時点でNullターミネイトされて
しまうので、null区切りされている文字配列データの取得には使用できない
と思っています。


StringBuilderはそうですが、Stringはそうではありません。\0の文字も受け取ることができます。少なくともUnmanagedType.VBByRefStrでマーシャリングするときは。

ですから
引用:

その為、同じ1byteのBYTE型配列で取得し、文字コードをSJIS(C++)から
Unicode(VB.NET)へ変換し、出力していました。


ですから、String型で受け取ればこの部分の処理はマーシャラに任せることができます。

引用:

★いろいろなホームページを見ましたが、皆StringBuilderを使用していました。


私もUnmanagedType.VBByRefStrは最近知りました

引用:

★これは、VB.NETで使用する文字コードは全てUnicodeであると言う事を
伝えたい文章だと思っています。間違っていますか?


VB.NETで使用する文字がUnicodeであるのは事実ですが、伝えたいことではありません。
C言語においては、文字というのはあくまでバイト列です。
ですから.NETからC言語にString型を渡すとなるとバイト列に変換しなければなりません。
またC言語から.NETに文字列としてバイト列を受け取るときも変換しなければなりません。
その変換にどの文字コードを使用するかというのがDeclare構文におけるAuto/Ansi/Unicodeの機能(の一つ)、と言うことです。
Unicodeを指定したとき、.NETの文字列"abc"をC言語の関数に渡すと、{0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x00, 0x00}というバイト列としてC言語の関数は受け取ります。
Ansiを指定したときは{0x61, 0x62, 0x63, 0x00}というバイト列に変換されます。
.NETが受け取るときは丁度この逆です。

引用:

★ここが知りたいです。
今回、Null区切りされた文字配列データ(SJIS)をアンマネージコードより
取得したいと思っています。
しかし、文字コードをマーシャリングしてString型に格納すると0ターミネイト
(null以降のデータは切り捨て)されてしまうので、どのような方法で取得が可能
あるか?


初めに言ったとおり、\0文字もそのままStringインスタンスの中に入ります。ターミネイトはされません。
//もちろん事前に十分な領域を持たせて(埋めて)いた場合、ですが。
普通に\0でSplitすれば目的のString配列が取得できるでしょう。
ターミネイトされているという根拠がVSのローカルで表示されている値やTextBoxの出力であるのなら、それは単にそれらのコントロールの制限で\0文字以降を表示できないだけである、という可能性を考慮して下さい。

引用:
コード:
<MarshalAs(UnmanagedType.LPArray, _
  ArraySubType:=UnmanagedType.LPStr, _
  SizeConst:=100)> ByVal FileList() As String
) As Integer



\0で区切られた文字列は別に配列ではありませんから、LPArrayを指定するのは無意味でしょう。
Byte配列で受け取って自前でデコードするか、Stringでマーシャラにデコードを任せるか。
いずれにせよ文字列(またはバイト配列)から各文字列に分解するのはプログラマの仕事です。
aacute
常連さん
会議室デビュー日: 2005/08/25
投稿数: 30
投稿日時: 2005-08-30 19:58
引用:

taaさんの書き込み (2005-08-30 13:32) より:
TextBox1.AppendText(str)


これを↓に変えてみてください。
コード:
        TextBox1.AppendText(str.Replace(char.MinValue," "c))


taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-30 20:21
すみません。
Jubeiさんのプログラムコード通り、
「Dim strs() As String = str.Split(Char.MinValue)」であれば、
正常にNull文字でSplitができました。
ありがとうございます。

只、なぜ私のプログラムでは正常に動作していなかったかと
いうと、セパレート値を"Nothing" or "vbNull" or &H0等で
指定していたからです。
その為、String型もStringBuilderと同様にNULLターミネイト
していると思っていました。
(ただし、セパレート値を"Char.MinValue"に指定すると
正常に動作する明確な理由はまだわかっていません。)

ご迷惑をお掛けしました。

私は現在、Hongliangさんからの回答を元にマーシャリング機能を
使用し、null区切りされた文字配列の取得方法を調べていますので
わかり次第、こちらも書き込み致します。
色々と、どうもありがとうございました。

[quote]
Jubeiさんの書き込み (2005-08-30 17:05) より:
諸農です。

引用:

ん??本当にそうなったのですか?

次のコードで試してみましたが、
結果として、

空白行
日本語
日本語日本語

の結果を得ることが出来ましたよ。


もしかして求めているものが違うのかな?



★間違っていません。合っています。
私のやり方が悪かっただけです。



[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 20:24 ]
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-30 20:26
引用:

aacuteさんの書き込み (2005-08-30 19:58) より:
引用:

taaさんの書き込み (2005-08-30 13:32) より:
TextBox1.AppendText(str)


これを↓に変えてみてください。
コード:
        TextBox1.AppendText(str.Replace(char.MinValue," "c))





★わざわざ、ありがとうございます。
区切り文字が"char.MinValue"であれば、Splitで正常に動作しました。

スキルアップ/キャリアアップ(JOB@IT)