C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

間違いを教えてください。

[トピック内 15 記事 (1 - 15 表示)]  << 0 >>

■85599 / inTopicNo.1)  間違いを教えてください。
  
□投稿者/ 夜叉丸 (79回)-(2017/11/09(Thu) 16:57:18)

分類:[.NET 全般] 


byte[] valuearray;

string strvalue = "";
for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++)
strvalue += (char)valuearray [isnum];

↓↓↓↓↓
逆変換したのですが

byte[] valuearray = new byte[strvalue .Length];
for (int inum = 0; inum < strvalue.Length; inum++)
valuearray[inum] = (byte)strvalue[inum];

元のデータと差異が生じています。
どうしてでしょうか?
どうすればよいのでしょうか?

引用返信 編集キー/
■85600 / inTopicNo.2)  Re[1]: 間違いを教えてください。
□投稿者/ 本醸造 (4回)-(2017/11/09(Thu) 18:11:29)
No85599 (夜叉丸 さん) に返信

valuearrayに適当な値を入れて試してみましたけど、問題なかったですよ
https://paiza.io/projects/MLIYlrYhduaT_hTReS25vg

valuearrayには何が入ってるんですか? valuearrayが怪しいと思います!
引用返信 編集キー/
■85608 / inTopicNo.3)  Re[1]: 間違いを教えてください。
□投稿者/ Jitta (334回)-(2017/11/09(Thu) 23:10:29)
No85599 (夜叉丸 さん) に返信
>
> byte[] valuearray;
>
> string strvalue = "";
> for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++)
> strvalue += (char)valuearray [isnum];
>
> ↓↓↓↓↓
> 逆変換したのですが
>
> byte[] valuearray = new byte[strvalue .Length];
> for (int inum = 0; inum < strvalue.Length; inum++)
> valuearray[inum] = (byte)strvalue[inum];
>
> 元のデータと差異が生じています。
> どうしてでしょうか?
> どうすればよいのでしょうか?
>

ん?
valuearray が20こ配列として、
全部の値が0だったら、
strvalueはどうなるんだ?
引用返信 編集キー/
■85609 / inTopicNo.4)  Re[1]: 間違いを教えてください。
□投稿者/ Azulean (896回)-(2017/11/09(Thu) 23:42:38)
2017/11/09(Thu) 23:45:38 編集(投稿者)

No85599 (夜叉丸 さん) に返信
> string strvalue = "";
> for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++)
> strvalue += (char)valuearray [isnum];

なぜ、string に入れるのでしょうか。
string は文字の集合であり、文字ではない byte 型を無理矢理変換して入れるようなものではありませんよ。
byte の可変長配列が欲しいのなら、List<byte> の方が良いでしょう。

// なお、char は 2 バイトです。

> 逆変換したのですが

元の strvalue そのままなのか、どこかを経由したのかは明らかにしておいた方が良いと思います。
byte を無理矢理変換した string なので、経由した内容次第ではデータが化けることもあると予想されるため。

> 元のデータと差異が生じています。
> どうしてでしょうか?

データが化けて、255 を超える値があるとか、0 を下回る値があるとか。
あるいは、0 が終端文字と誤解されて途切れているとか。

> どうすればよいのでしょうか?

個人的に思うのは、string を使うのをやめるべきでしょうね。
なぜこんな処理を書いているのか、目的・背景・理由を明らかにしてもらった方が良いでしょう。


No85608 (Jitta さん) に返信
> valuearray が20こ配列として、
> 全部の値が0だったら、
> strvalueはどうなるんだ?

\0 が 20 個入った string になります。
.NET の string はヌル文字終端ではなく、長さ管理なので。
引用返信 編集キー/
■85610 / inTopicNo.5)  Re[2]: 間違いを教えてください。
□投稿者/ 夜叉丸 (80回)-(2017/11/10(Fri) 08:28:03)
文字列にして、INI ファイルに Key=Value の
Value として登録するために
関数を2つ作りました。

byte[] の状態まではうまく変換できたのですが、
String にすると変換できずにエラーメッセージが出てきたので
byte[] → string か、byte[] → dynamic のどちらかで
何かがおかしいのかと思ったのです。
現状では Value に DataTable型 のデータを指定しています。


private string DynamicToString(dynamic Value)
{
	// dynamic → byte[]
	MemoryStream sstream = new MemoryStream();
	IFormatter formatter = new BinaryFormatter();
	formatter.Serialize(sstream, Value);

	// byte[] → string
	string strvalue = "";
	for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++) strvalue += (char)sstream.ToArray()[isnum];

	return (strvalue);
}

private dynamic StringToDynamic(string Value)
{
	try
	{
		if (Value == "") return (null);

		// string → byte[]
		byte[] valuearray = new byte[Value.Length];
		for (int inum = 0; inum < Value.Length; inum++) valuearray[inum] = (byte)Value[inum];

		// byte[] → dynamic
		MemoryStream dstream = new MemoryStream(valuearray);
		IFormatter formatter = new BinaryFormatter();
		return (formatter.Deserialize(dstream));
	}
	catch(Exception ex)
	{
		MessageBox.Show(ex.Message);
		return (null);
	}
}



引用返信 編集キー/
■85611 / inTopicNo.6)  Re[3]: 間違いを教えてください。
□投稿者/ 夜叉丸 (81回)-(2017/11/10(Fri) 08:41:47)
ちなみに表示されるエラーメッセージは以下のメッセージです。

「バイナリストリーム'46'に、有効なBinaryHeaderが含まれていません。
シリアル化と逆シリアル化の途中で、無効なストリームまたは
オブジェクトのバージョン変更が発生した可能性があります。」

また、今テスト的に変換しようとしているデータは以下のような文字列になっています。
DataTable で 列は Start, End で 1行目に それぞれ "A1", "B1" を入れたものです。

"\0\u0001\0\0\0&yuml;&yuml;&yuml;&yuml;\u0001\0\0\0\0\0\0\0\f\u0002\0\0\0NSystem.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\u0005\u0001\0\0\0\u0015System.Data.DataTable\u0003\0\0\0\u0019DataTable.RemotingVersion\tXmlSchema\vXmlDiffGram\u0003\u0001\u0001\u000eSystem.Version\u0002\0\0\0\t\u0003\0\0\0\u0006\u0004\0\0\0&Uacute;\u0005<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<xs:schema xmlns=\"\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n <xs:element name=\"Table1\">\r\n <xs:complexType>\r\n <xs:sequence>\r\n <xs:element name=\"START\" type=\"xs:string\" msdata:targetNamespace=\"\" minOccurs=\"0\" />\r\n <xs:element name=\"END\" type=\"xs:string\" msdata:targetNamespace=\"\" minOccurs=\"0\" />\r\n </xs:sequence>\r\n </xs:complexType>\r\n </xs:element>\r\n <xs:element name=\"tmpDataSet\" msdata:IsDataSet=\"true\" msdata:MainDataTable=\"Table1\" msdata:UseCurrentLocale=\"true\">\r\n <xs:complexType>\r\n <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\" />\r\n </xs:complexType>\r\n </xs:element>\r\n</xs:schema>\u0006\u0005\0\0\0&Atilde;\u0002<diffgr:diffgram xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\" xmlns:diffgr=\"urn:schemas-microsoft-com:xml-diffgram-v1\">\r\n <tmpDataSet>\r\n <Table1 diffgr:id=\"Table11\" msdata:rowOrder=\"0\" diffgr:hasChanges=\"inserted\">\r\n <START>A1</START>\r\n <END>B1</END>\r\n </Table1>\r\n </tmpDataSet>\r\n</diffgr:diffgram>\u0004\u0003\0\0\0\u000eSystem.Version\u0004\0\0\0\u0006_Major\u0006_Minor\u0006_Build\t_Revision\0\0\0\0\b\b\b\b\u0002\0\0\0\0\0\0\0&yuml;&yuml;&yuml;&yuml;&yuml;&yuml;&yuml;&yuml;\v"
引用返信 編集キー/
■85612 / inTopicNo.7)  Re[4]: 間違いを教えてください。
□投稿者/ 夜叉丸 (82回)-(2017/11/10(Fri) 08:57:20)
なぜ、byte[] string の相互変換ができないのかがわからないので
教えてください。

ひょっとしたらたまたまかもしれませんけど
List<string> や String などは普通に変換できましたし
問題は起こりませんでした。
今のところ DataTable だけがエラー発生します。


逃げ道としては今のところ対応するために
byte [A][B][C] → string '414243'
として16進表示文字列にするしかないかと考えていますが、
他にもっといい案があれば教えて下さい。

引用返信 編集キー/
■85613 / inTopicNo.8)  Re[5]: 間違いを教えてください。
□投稿者/ 夜叉丸 (83回)-(2017/11/10(Fri) 09:40:55)
以下のように変更するととりあえずは動作するようになりましたが、
文字サイズが2倍になっちゃいますし、
入力されているデータの中身は全く分からくなりました。

byte[] string は何が原因で使えないんでしょうか?


string strvalue = "";
for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++) strvalue += ((char)sstream.ToArray()[isnum]).ToString();
↓↓↓↓↓
string strvalue = "";
for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++) strvalue += (sstream.ToArray()[isnum]).ToString("X2");



byte[] valuearray = new byte[Value.Length];
for (int inum = 0; inum < Value.Length; inum++) valuearray[inum] = (byte)Value[inum];
↓↓↓↓↓
int inum = 0;
byte[] valuearray = new byte[Value.Length / 2];
for (; inum < valuearray.Length; inum++) valuearray[inum] = (byte)Convert.ToInt32(Value.Substring(inum * 2, 2), 16);
引用返信 編集キー/
■85616 / inTopicNo.9)  Re[5]: 間違いを教えてください。
□投稿者/ 魔界の仮面弁士 (1456回)-(2017/11/10(Fri) 12:04:11)
2017/11/10(Fri) 12:06:01 編集(投稿者)

No85612 (夜叉丸 さん) に返信
> なぜ、byte[] string の相互変換ができないのかがわからないので
> 教えてください。

Azulean さんも書かれていますが、念のために確認。
char/Char は「16 bit(2 バイト)長」のデータで、
byte/Byte は「8 bit(1 バイト)長」のデータですが、
その点の認識に差異はありますか?

そして
 char a1 = 'a';
 char a1 = '\u0061';
 char a1 = (char)0x61;
 char a1 = (char)97;
はいずれも同じ意味です。

-----

たとえば、まずこんなデータがあったとします。

char a1 = '\u0061'; // Latin Small Letter A
char a2 = '\u03B1'; // Greek Small Letter Alpha
char a3 = '\uff41'; // Fullwidth Latin Small Letter A
string str = new string(new char[] { a1, a2, a3 });


データ内容が分かりやすくなるよう、上記は意図的に
16 進数コードで表記していますが、要するに、
 string str = "aαa";
を回りくどく書いているだけです。


さて、上記の 3 文字(str 変数)を byte[] 配列にした場合、
どのようなバイナリに変換されることを期待しておられますか?



(1) UCS-2 あるいは UTF-16 バイナリ相当の 6 バイト
  61-00-B1-03-41-FF

(2) UTF-16BE バイナリ相当の 6 バイト
  00-61-03-B1-FF-41

(3) UTF-8 バイナリ相当の 6 バイト
  61-CE-B1-EF-BD-81

(4) CP932 あるいは Shift_JIS バイナリ相当の 5 バイト
  61-83-BF-82-81

(5) EUC-JP バイナリ相当の 5 バイト
  61-A6-C1-A3-E1

(6) ISO-2022-JP バイナリ相当の 11 バイト
  61-1B-24-42-26-41-23-61-1B-28-42


たとえば (1) を望むなら、
 string str = "aαa";
 byte[] bin = System.Text.Encoding.Unicode.GetBytes(str);
で変換でき、それを復元するために
 string result = System.Text.Encoding.Unicode.GetString(bin);
と書けます。
引用返信 編集キー/
■85617 / inTopicNo.10)  Re[3]: 間違いを教えてください。
□投稿者/ Azulean (897回)-(2017/11/10(Fri) 12:32:27)
No85610 (夜叉丸 さん) に返信
> 文字列にして、INI ファイルに Key=Value の
> Value として登録するために
> 関数を2つ作りました。

BinaryFormatter でシリアライズした内容を ini ファイルに書き出すシナリオという理解で問題ないですか?

BinaryFormatter の出力で得られる byte 配列をそのまま char 配列、そして string に変換すると、表示できない文字になる部分があります。
たとえば、0 は扱う場所によっては文字列の終端とみなされます。
ini ファイルを扱う API も 0 を終端とみなしますので途切れるでしょうね。
その他、文字コードの都合で ini ファイルで扱えないバイトがあるので、ini に書き出すなら char にキャストするのではなく、何らかのエンコード(符号化)が必要です。
16 進数にするのは1つの手ですし、BASE64 を使うのも選択肢になるかもしれません。(= が大丈夫かは不安ですが)

なお、符号化によって、中身がパッとみてわからなくなる点は避けられません。ini ファイルを使う以上は仕方ないかと。
引用返信 編集キー/
■85618 / inTopicNo.11)  Re[4]: 間違いを教えてください。
□投稿者/ 魔界の仮面弁士 (1457回)-(2017/11/10(Fri) 13:29:40)
No85617 (Azulean さん) に返信
> BASE64 を使うのも選択肢になるかもしれません。(= が大丈夫かは不安ですが)

BASE64 の = が現われるのはパディング部だけなので、
パディングが不要な実装、たとえば base64url が向いているかもしれません。
https://ja.wikipedia.org/wiki/Base64


// 保存したいバイナリ
byte[] inBinary = { 0x96, 0xe9, 0x8d, 0xb3, 0x8a, 0xdb };

// ini ファイルで保存可能な base64url 文字列に変換
string iniString = inBinary.ToIniString();

// 元のバイナリに復元
byte[] outBinary = iniString.ToIniBinary();
 



static class Base64Extensions
{
 public static string ToIniString(this byte[] bin)
 {
  return Convert.ToBase64String(bin).TrimEnd('=')
   .Replace('/', '_').Replace('+', '-');
 }
 
 public static byte[] ToIniBinary(this string str)
 {
  int p = 4 - (str.Length % 4);
  string s = str;
  if (p != 4) { s += new string('=', p); }
  s = s.Replace('_', '/').Replace('-', '+');
  return Convert.FromBase64String(s);
 }
}

ini ファイルなら、+ → _ や / → - の置き換えは無くても大丈夫かも。
引用返信 編集キー/
■85619 / inTopicNo.12)  Re[5]: 間違いを教えてください。
□投稿者/ 夜叉丸 (84回)-(2017/11/10(Fri) 13:55:14)
No85618 (魔界の仮面弁士 さん) に返信

はっきりとは理解できていないのですが、
char を介してコピーを行うと
文字列としての特殊な処理がされるということでしょうか?

何らかの方法にて byte[n] → string[n] のコピーは可能なのでしょうか?


引用返信 編集キー/
■85620 / inTopicNo.13)  Re[3]: 間違いを教えてください。
□投稿者/ とっちゃん (471回)-(2017/11/10(Fri) 15:02:57)
No85619 (夜叉丸 さん) に返信

> はっきりとは理解できていないのですが、
> char を介してコピーを行うと
> 文字列としての特殊な処理がされるということでしょうか?
>
.NET の char は、2バイトのデータ型です。
C++ の wchar_t と同等で、データサイズは unsigned short と同じです。
また、格納されているものは文字コードという前提で作られています。

それと、string や、"..." は文字列型や文字列であって、文字配列ではありません。

C や C++ は"..."は文字列と表記しますが実際は「\0で終わる文字の配列」という特殊な配列です。
std::string のように文字列型を用いる場合でも、リテラルなデータは文字の配列であることに変わりはありません。

ソースコードというテキストデータ上では同じ表現を用いていますが
コンパイルされ、実行中のメモリの状態は異なる概念であることも注意が必要でしょう。



> 何らかの方法にて byte[n] → string[n] のコピーは可能なのでしょうか?


「n個の要素を持つバイトの配列」を 「n個の要素を持つ文字列の配列」にコピーするのですか?
それとも
「バイト配列のn番目の要素」を「stringのn番目の要素」としてコピーするのですか?

どちらとも読めますが、これまでここについて明確にこうであるとは記述されていないように思います。
(だからプログラムも怪しい)

INIファイルにDataTableのレコード(あるいはデータブロックそのもの)を格納するのはどうなの?とも思いますが。。。

自分の過去に携わってきたものとか、ここ30年余りのINIファイルの扱われ方などを考慮すると

INI形式のファイルの特定のエントリーに、バイナリデータをバイナリチックな文字列として格納しておく
という可能性が考えられます。

構造としては... ■No85610 の DynamicToString の文字列化部分は

// byte[] → string
string strvalue = "";
for (int isnum = 0; isnum < sstream.ToArray().Length; isnum++) strvalue += (char)sstream.ToArray()[isnum];

ではなく、
foreach( var ch in sstream.ToArray() ) strvalue += ch.ToString( "X2" );
という感じが本来意図していた変換処理なのではないでしょうか?

これだと byte[] の2倍になりますが30年くらい前のコードでもよく使われていた形式なので
テキストデータで維持するのが目的になってるなら使うこともできると思います。

個人的にはほかの人も書いているように BASE64 などの形式のほうがいいと思いますが
互換性問題が出る気もするのでとりあえずは言及しないことにします。

引用返信 編集キー/
■85621 / inTopicNo.14)  Re[6]: 間違いを教えてください。
□投稿者/ furu (130回)-(2017/11/10(Fri) 15:10:48)
No85619 (夜叉丸 さん) に返信
> ■No85618 (魔界の仮面弁士 さん) に返信
>
> 何らかの方法にて byte[n] → string[n] のコピーは可能なのでしょうか?
>
byte[n] →stringの間違い?

byte(符号なし8ビット整数)からchar(16ビット数)へはコピー可能なので
byte[n]からstringへのコピーは可能です。

逆(string→byte[n])は
各文字がU+0000 〜 U+00FFの範囲であるならば可能です。

引用返信 編集キー/
■85623 / inTopicNo.15)  Re[4]: 間違いを教えてください。
□投稿者/ 夜叉丸 (85回)-(2017/11/10(Fri) 16:40:46)
ありがとうございました。

逆変換が文字として処理されるためうまくいかないんですね。

やはり、16進数に変換して文字列にします。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ