none
16ビット単位でリトルエンディアンにしたい RRS feed

  • 質問

  • いつもお世話になります。
    VisualC#2010、WindowsXPでの動作について質問があります。

    E0 FF 06 00 00 00 02 00 22 01 E1 FF

    というようなbyte[]配列(bs)を

    FFE0 0006  0000 0002  0122  FFE1

    のようなリトルエンディアンの文字列にしたいです。
    if (BitConverter.IsLittleEndian) {
     Array.Reverse(bs);
     string littleendiantext = BitConverter.ToString(bs);
    }
    とすると、

    FF-E1-01-22-00-02-00-00-00-06-FF-E0

    のように、配列全体を逆転してしまいます。
    どのようにしたらよいでしょう。
    あと、区切りを16ビットずつにしたいです。
    あわせてご示唆ください。

    2010年11月17日 13:56

回答

  • Deleted
    • 回答としてマーク d-kot 2010年11月18日 12:01
    2010年11月17日 15:21
  • 無条件に反転するパターンですよね。一応私も書いてはいます。

    Console.WriteLine( string.Join( " ", Enumerable.Range( 0, bs.Length / 2 ).Select( i => string.Format( "{1:X2}{0:X2}", bs[i*2+0], bs[i*2+1] ) ) ) );

    こんな感じになりました。

    • 回答としてマーク d-kot 2010年11月18日 12:40
    2010年11月17日 15:25
  • var bytes = new byte[] { 0xE0, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x01, 0xE1, 0xFF };
    var list = bytes.Where((v,i)=>i%2==1)
                           .Zip( bytes.Where((v,i)=>i%2==0),
                                  (b2,b1)=>b2.ToString("x2")+b1.ToString("x2"));

    こういうのもありだと思います。2つの集合に分割しといてZipでくっつける。
    文字列かするならこれをさらに

    Console.Write(string.Join(" ",list.ToArray()));

    でしょうか。

    • 回答としてマーク d-kot 2010年11月18日 12:41
    2010年11月18日 1:38

すべての返信

  • 「リトルエンディアンにしたい」という記述は意味が分かりませんでした。
    単に反転したいだけですか? (だとするとエンディアンは関係ないですし…)
    それとも実行中のプロセッサのエンディアンに応じて反転したりしなかったりということですか? (だとするとリトルエンディアンになるわけではありませんし…)

    2010年11月17日 14:57
  • Deleted
    • 回答としてマーク d-kot 2010年11月18日 12:01
    2010年11月17日 15:21
  • 無条件に反転するパターンですよね。一応私も書いてはいます。

    Console.WriteLine( string.Join( " ", Enumerable.Range( 0, bs.Length / 2 ).Select( i => string.Format( "{1:X2}{0:X2}", bs[i*2+0], bs[i*2+1] ) ) ) );

    こんな感じになりました。

    • 回答としてマーク d-kot 2010年11月18日 12:40
    2010年11月17日 15:25
  • IsLittleEndian を確認しているのはなぜでしょう? データが BigEndian に固定で実行環境が Little/Big のどちらかわからないので、実行環境にあわせたいということでしょうか?

    であれば、IPAddress.NetworkToHostOrder が使用できそうですね。

    2010年11月17日 23:57
  • var bytes = new byte[] { 0xE0, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x01, 0xE1, 0xFF };
    var list = bytes.Where((v,i)=>i%2==1)
                           .Zip( bytes.Where((v,i)=>i%2==0),
                                  (b2,b1)=>b2.ToString("x2")+b1.ToString("x2"));

    こういうのもありだと思います。2つの集合に分割しといてZipでくっつける。
    文字列かするならこれをさらに

    Console.Write(string.Join(" ",list.ToArray()));

    でしょうか。

    • 回答としてマーク d-kot 2010年11月18日 12:41
    2010年11月18日 1:38
  • Enumerable.Zip()を使うということは.NET 4ですが、だとしたらstring.Join()のためにToArray()する必要はありませんよ。

    2つずつのペアを作るアプローチだと

    var i = 0;
    Console.WriteLine(string.Join(" ", bs.GroupBy(_ => i++ / 2).Select(g => string.Format("{1:X2}{0:X2}", g.Cast<object>().ToArray()))));

    なるほど。どちらにしても単にバイト順を入れ替えたいのか、エンディアンを意識して入れ替えたいのかはっきりしないことには、ですが。それとArray.Reserve()を持ち出す人にLinqが通用するかも気になるところ。

    2010年11月18日 4:14
  • 佐祐理さん、説明がわけわからずすみません。

    E0 FF 06 00 00 00 02 00 22 01 E1 FF

    FFE0 0006  0000 0002  0122  FFE1
    としたい、ということだけです。

    Array.Reserve()を持ち出す人にLinqが通用するかも気になるところ。
    と、いろいろご心配いただいておりますが、
    Linqは好きです。
    初心者にはむしろ、Linqのほうが、すっきり書けて理解しやすいように思います。
    わからなければ何度でもききますので、ご心配無用で、最良のコードを、コードで教えていただければと。

    ちなみに自分では、
    int lowhigh = 1;
    string buffer = "";
    foreach(byte b in bs) {
     string sixteen = int.Parse(b.ToString()).ToString("X").ToLower();
     if (sixteen.Length == 1) sixteen = "0" + sixteen;
     if (lowhigh%2 == 1) buffer = sixteen;
     else log += "0x" + sixteen + buffer + " ";
     lowhigh++;
    }
    のようにして実現できていました。
    質問したときから、0xを追加してffe0のように小文字化してみました。

    2010年11月18日 12:40
  • ありがとうございました。これでできるのを確認しました。
    これで、0xを追加しようとしたら、
    .Aggregate(("0x" + s1, s2) => s1 + " " + s2);
    .Aggregate((s1, s2) => "0x" + s1 + " " + s2);
    とかためしてだめだったのですが、追加するとしたらどこに追加すればよいでしょう?
    あ。
    .Select(v => "0x" + v.ToString("X04"))
    でできました。

    あと、
    << 8
    って、ひょっとして16ビットのうちの8ビットとかの意味ですか?

    2010年11月18日 12:40
  • データが、
    E0 FF 06 00 00 00 02 00 22 01 E1 FF
    に固定で、これを解釈するためには、
    FFE0 0006  0000 0002  0122  FFE1
    にしなければならない、ということです。
    これをエンディアン変換というのだと思っていたのですが、
    言葉については自信なしです。すみません。
    2010年11月18日 12:40
  • これもできるのを確認しました。
    みなさんありがとうございました。
    2010年11月18日 12:41
  • ひょっとして、ToStringするときに、x2とすると小文字で2桁、x04とすると、小文字で4桁(2桁ずつ0を追加する?)、ってことでしょうか。
    するとひょっとして、ToStringするときに、0xを先頭に追加するオプションもあるんですか?
    2010年11月18日 13:13
  • string.Format()に渡すフォーマット文字列は複合書式設定 に説明がありますが、

    {index[,alignment][:formatString]}

    と3つのフィールドに分かれます。このformatStringにはいろいろ指定できて標準の数値書式指定文字列 で説明されています。

    "0x"を付け加えたいのなら0x{0:x4}のように書式設定の外に書くまでです。
    なお、渋木宏明さん・竹原貴司さんが使われているToString()メソッドではformatString部分を指定します。(その分、表現力が弱いです。)

    2010年11月18日 13:32
  • Enumerable.Aggregate()は折り畳むというか、

    new[]{ 1, 2, 3, 4 }.Aggregate( ( x, y ) => x + y )

    ( ( ( 1 + 2 ) + 3 ) + 4 )

    を意味します。

     

    渋木宏明さんへ

    "X04"のゼロは不要です。libcのprintf()と異なり4自体にゼロパディングするという意味が含まれています。

    2010年11月18日 13:39
  • 補足説明ありがとうございました。
    .Select(v => "0x" + v.ToString("x4"))
    で、動作することを確認できました。

    .Select( i => string.Format( "0x{1:x2}{0:x2}",

    でも同様の結果を得ました。

    string.Join( " ", Enumerable.Range( 0, bs.Length / 2 )
    .Select( i => "0x" + bs[i*2+1] .ToString("x2") + bs[i*2+0].ToString("x2"))) ;
    と書けるのも確認しました。

    2010年11月18日 14:02
  • 素直にMemoryStream から BinaryReader で読めばいいですよ。

        class Program
        {
            static void Main(string[] args)
            {
                var array = new byte[] { 0xE0, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x01, 0xE1, 0xFF };
                var memStream = new MemoryStream(array);
                BinaryReader binaryReader = new BinaryReader(memStream);
                for (int i = 0; i < memStream.Length; i += 2)
                {
                    Console.Write("{0:X04} ", binaryReader.ReadInt16());
                }
                Console.ReadLine();
            }
        }

    Kazuhiko Kikuchi
    2010年11月26日 4:33
  • byte列から文字列を取得したいのであればEncodingクラスにGetStringというメソッドがあります。例えば、Unicodeからであれば

    string text = Encoding.Unicode.GetString(bytes);

    のようにし、SJISの場合だったら

     string text = Encoding.GetEncoding("shift_jis").GetString(bytes);
    

    といった感じになります。

     


     

    XNA Framework Developer

    2010年11月26日 4:56