文字列を16進コードに変換するには

解決


sato  2003-02-14 22:53:55  No: 51046

VC++でDLLを作成しています。
その中で、コード変換をしたいのですが、よく分かりません。
文字列「1Aaあいう...」を「314161...」のように16進コードに
変換するには、どのようにしたら良いのでしょうか。

よろしくお願いいたします。


YuO  2003-02-14 23:54:42  No: 51047

出力の型は何ですか?


sato  2003-02-16 00:45:42  No: 51048

出力の型はcharの配列にしようと思っています。
sprintfを使って試してみたところ大体出来たのですが、
「フ」などを変換したとき「FFFFFFCC」となってしまい、
後々の処理で16進から元の文字列に戻すときに
「FF」「FF」「FF」「CC」なのか、「FFFFFFCC」なのか
判断ができなくて困っています。
どのようにしたら良いのでしょうか。
ちなみに、16進に変換する文字列というのは、バイナリデータです。
よろしくお願いいたします。


YuO  2003-02-16 01:31:22  No: 51049

sprintfを使うなら,unsigned charへキャストする必要があります。
汎整数拡張によって,signed char/shortはintに,unsigned char/unsigned shortはunsignedにそれぞれ変換されるからです。

ちなみに,Cではchar/shortでの演算は全てintで行われます。
なので,
void func (short);
なんてのを使うと,処理系次第では呼び出しごとに警告を発してくれます。

自力で書いてしまっても大したものではないですけどね……。
#Cだと自力で書いた方が簡単かも。
とりあえず,適当に書いてみました。

/* C言語用 */

#define BYTE_PER_CHARACTER ((CHAR_BIT + 3) / 4) /* 大抵のコンパイラでは 2 */

/* buffer : 出力先
   mem    : 入力
   size   : 入力の大きさ
   戻り値 : buffer。必要な大きさはsize * BYTE_PER_CHARACTER + 1。 */
char * convertToHex (char * buffer, const void * mem, size_t size)
{
    const unsigned char * src = mem;
    size_t i;

    for (i = 0; i < size; ++i) {
#if BYTE_PER_CHARACTER == 2
        buffer[i * 2] = "0123456789ABCDEF"[src[i] / 0x10];
        buffer[i * 2 + 1] = "0123456789ABCDEF"[src[i] % 0x10];
#else /* 1バイトが8ビットより大きい処理系 */
        unsigned char data;
        int j;

        data = src[i];
        for (j = BYTE_PER_CHARACTER - 1; j >= 0; --j) {
            buffer[i * BYTE_PER_CHARACTER + j] = "0123456789ABCDEF"[data % 0x10];
            data /= 0x10;
        }
#endif
    }
    buffer[i * BYTE_PER_CHARACTER] = 0;

    return buffer;
}

// C++言語用

// buffer : 出力先
// first  : 入力バイト列の先頭
// last   : 入力バイト列の末尾
// 戻り値 : bufferに書き込んだ文字の数。
//   必要なバッファサイズは,(std::numeric_limits<unsigned char>::digits + 3) / 4 * std::distance<const unsigned char *>(first, last)で求められる。
std::size_t convertToHex (char * buffer, const void * first, const void * last)
{
    std::ostringstream oss("");

    oss << std::hex << std::setfill('0') << std::uppercase;

    for (const unsigned char * first_ = static_cast<const unsigned char *>(first); first_ != last; ++first_) {
        oss << std::setw((std::numeric_limits<unsigned char>::digits + 3) / 4) << static_cast<unsigned int>(*first_);
    }

    const std::string & temp = oss.str();
    temp.copy(buffer, temp.length());

    return temp.length();
}


sato  2003-02-16 23:56:41  No: 51050

YuOさんのC言語用の変換関数を使わせていただきました。

で、その後、16進に変換したものを元のバイナリデータに
戻す処理を考えてみたのですが、
うまくいきません。
修正点などご教授お願いいたします。

以下は作成したソースです。

// 2つのChar配列を1つのChar配列に結合
  // 16進変換後のchar配列
  unsigned char buffer[1024];
  // 16進から元のバイナリデータを格納するためのchar配列
  unsigned char chWk2[512];
  unsigned char chWk1;
  int k=0;
  int j=0;

  for(;k < 1024 ; k++ ){
    if( buffer[k] != '\0' ){
      if( k % 2 == 0 ){
        chWk1 = (buffer[k] & 0x0F) << 4;//左に4ビットシフトのつもり。
      }else{
        chWk2[j] = chWk1 | (buffer[k] & 0x0F); //結合
        j++;
      }
    }else{
      break;
    }
  }


YuO  2003-02-17 00:39:38  No: 51051

そりゃ,文字列に変換してしまうと,それをビット演算だけで戻すのは不可能ですよ。
#強引に出来ないことはないですけど……。

例えば,VC++においては,
0 : 0x30
1 : 0x31
...
9 : 0x39
A : 0x41
B : 0x42
...
F : 0x46
ですから,buffer[k] & 0x0Fでは,
0 : 0
1 : 1
...
9 : 9
まではともかく,
A : 1
B : 2
...
F : 6
となってしまいます。
というわけで,switchを使ったり,strtodを使ったりする必要があります。


sato  2003-02-17 22:27:49  No: 51052

16進に変換したものを元のバイナリデータに戻す処理を
以下のように修正してみました。

しかし、16進が00と続くとき、bufferがそこで閉じてしまいます。
これが「ビット演算だけで戻すのは不可能」ということなのでしょか。
別のやり方が思い浮かばないのですが。。
よろしくお願いいたします。

void ConvertToBin(char *buffer, const char *mem, size_t size)
{

  const unsigned char * src = (const unsigned char *)mem;
  unsigned char chWk;

  size_t k = 0;
  int j = 0;

  for (; k < size; k++) {
    if (src[k] != '\0') {
      // インデックスが偶数の場合、下位4ビット取り出して上位4ビットにする
      if (k % 2 == 0) {
        // src[k]が'0'〜'9'の場合
        if ( src[k] <= '9'){
          chWk = (src[k] - '0') << 4;// 左に4ビットシフト
        
        // src[k]が'A'〜'F'の場合
        } else {
          chWk = (src[k] - 'A' + 10) << 4;// 左に4ビットシフト
        }

      // インデックスが奇数の場合、下位4ビット取り出して下位4ビットにする
      } else {
        // src[k]が'0'〜'9'の場合
        if (src[k] <='9') {
          buffer[j] = chWk + (src[k] - '0'); // 結合

        // src[k]が'A'〜'F'の場合
        } else {
          buffer[j] = chWk + (src[k] - 'A' + 10); // 結合
        }
        j++;
      }

    // '\0'セット
    } else {
      buffer[j]=src[k];
      break;
    }
  }
}


YuO  2003-02-17 22:44:14  No: 51053

> しかし、16進が00と続くとき、bufferがそこで閉じてしまいます。
> これが「ビット演算だけで戻すのは不可能」ということなのでしょか。

閉じる,というのが何を意味するのか分かりませんが……。
バイナリデータなのだから,

> // '\0'セット

に意味はないですよね。


sato  2003-02-17 22:57:06  No: 51054

例えば、memに「313233003435」とあった場合に、
bufferに「123」までは入るのですが、
それ以降、ループは回っていてmemを変換しているのですが、
bufferは"123"しか入らないという意味です。

なにか根本的にやっていることがおかしいのでしょうか。


RAPT  2003-02-17 23:42:28  No: 51055

> bufferは"123"しか入らないという意味です。
どうやって確認しましたか?  デバッガで、中身を確認しましたか?

当該配列の中身を、“田”となってるところをクリックして、
展開して1つ1つ確認してみたのですか?


YuO  2003-02-17 23:44:55  No: 51056

バイナリデータなのですから,00を特殊な文字とみなす必要はないでしょう。
デバッガでメモリを調べてみましたか?
ちゃんと,データは変換されているはずです。


sato  2003-02-17 23:50:23  No: 51057

申し訳ありません。
中身確かに入っていました。

タイトルの件については解決しました。

お答えくださった皆さん、ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加