VC++でDLLを作成しています。
その中で、コード変換をしたいのですが、よく分かりません。
文字列「1Aaあいう...」を「314161...」のように16進コードに
変換するには、どのようにしたら良いのでしょうか。
よろしくお願いいたします。
出力の型は何ですか?
出力の型はcharの配列にしようと思っています。
sprintfを使って試してみたところ大体出来たのですが、
「フ」などを変換したとき「FFFFFFCC」となってしまい、
後々の処理で16進から元の文字列に戻すときに
「FF」「FF」「FF」「CC」なのか、「FFFFFFCC」なのか
判断ができなくて困っています。
どのようにしたら良いのでしょうか。
ちなみに、16進に変換する文字列というのは、バイナリデータです。
よろしくお願いいたします。
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();
}
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;
}
}
そりゃ,文字列に変換してしまうと,それをビット演算だけで戻すのは不可能ですよ。
#強引に出来ないことはないですけど……。
例えば,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を使ったりする必要があります。
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;
}
}
}
> しかし、16進が00と続くとき、bufferがそこで閉じてしまいます。
> これが「ビット演算だけで戻すのは不可能」ということなのでしょか。
閉じる,というのが何を意味するのか分かりませんが……。
バイナリデータなのだから,
> // '\0'セット
に意味はないですよね。
例えば、memに「313233003435」とあった場合に、
bufferに「123」までは入るのですが、
それ以降、ループは回っていてmemを変換しているのですが、
bufferは"123"しか入らないという意味です。
なにか根本的にやっていることがおかしいのでしょうか。
> bufferは"123"しか入らないという意味です。
どうやって確認しましたか? デバッガで、中身を確認しましたか?
当該配列の中身を、“田”となってるところをクリックして、
展開して1つ1つ確認してみたのですか?
バイナリデータなのですから,00を特殊な文字とみなす必要はないでしょう。
デバッガでメモリを調べてみましたか?
ちゃんと,データは変換されているはずです。
申し訳ありません。
中身確かに入っていました。
タイトルの件については解決しました。
お答えくださった皆さん、ありがとうございました。
ツイート | ![]() |