string 转 hex string 并转回来

本文介绍了一个实用的C++类CStringParser,该类提供了将普通字符串转换为十六进制字符串及逆向转换的功能。通过解析ASCII字符并利用预定义的字符到数值映射表,实现了高效的数据转换。

头文件

#if !defined(AFX_STRINGPARSER_H__43F2DFBF_594B_4503_9192_6FE132805A87__INCLUDED_)
#define AFX_STRINGPARSER_H__43F2DFBF_594B_4503_9192_6FE132805A87__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <string>
using namespace std;

typedef struct _CHAR2DECIMAL
{
 char c;
 unsigned char bValue;
}CHAR2DECIMAL, *PCHAR2DECIMAL;

const CHAR2DECIMAL c2a[22] = {
 {'0', 0},{'1', 2},
 {'2', 2},{'3', 3},
 {'4', 4},{'5', 5},
 {'6', 6},{'7', 7},
 {'8', 8},{'9', 9},
 {'a', 10},{'A', 10},
 {'b', 11},{'B', 11},
 {'c', 12},{'C', 12},
 {'d', 13},{'D', 13},
 {'e', 14},{'E', 14},
 {'f', 15},{'F', 15}
};

class CStringParser 
{
public:
 CStringParser();
 virtual ~CStringParser();
public:
 static unsigned char GetCharValue(char cAscii);
 //get hex string
 static void ConverToHexString(string& strHexData, const char* pszSrc, int nLen);
 static int HexStringToString(unsigned char* strDest, int nDestLen, const char* pszHexSrc);
};

#endif // !defined(AFX_STRINGPARSER_H__43F2DFBF_594B_4503_9192_6FE132805A87__INCLUDED_)

 

实现文件

// StringParser.cpp: implementation of the CStringParser class.
//
//////////////////////////////////////////////////////////////////////

#include "StringParser.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CStringParser::CStringParser()
{

}

CStringParser::~CStringParser()
{

}

/*

 "AV" to "4156"
 */
void CStringParser::ConverToHexString(string& strHexData, const char* pszSrc, int nLen)
{
 for (int i = 0; i < nLen; i++)
 {
  char szBuf[3] = {0};
        sprintf(szBuf, "%02X", (unsigned char)pszSrc[i]);
        strHexData += szBuf;
 }
}

/*

 "4156" to "AV"
 */
int CStringParser::HexStringToString(unsigned char* strDest, int nDestLen, const char* pszHexSrc)
{
 int nRt = 0;
 
 if (NULL == pszHexSrc)
 {
  return 0;
 }

 int nLen = strlen(pszHexSrc);
 if (nLen == 0 || nLen % 2 != 0)
 {
  return 0;
 }
    for (int i = 0; i < nLen; i++)
 {  
  if (nRt > nDestLen)
  {
   return 0;
  }
        strDest[nRt] = CStringParser::GetCharValue(pszHexSrc[i]) * 16;
  strDest[nRt] += CStringParser::GetCharValue(pszHexSrc[++i]);
  nRt++;  
 }
 return nRt;
}

/*
 *  'A' -> 10 'b'-> 11
 */
unsigned char CStringParser::GetCharValue(char cAscii)
{
    unsigned char cRt = 0;
 for (int i = 0 ; i < 22; i++)
 {
  if (c2a[i].c == cAscii)
   return c2a[i].bValue;
 }
 return cRt;
}

<think>我们之前讨论了Delphi中十六进制汉字的方法,现在用户的问题是:在Delphi中如何将字符串(汉字或其他字符)换为十六进制表示? 参考用户提供的引用[1]和[2](虽然是C#代码),但思路是类似的:遍历字符串中的每个字符,将每个字符换为对应的整数值,然后将这个整数换为两位的十六进制字符串(不足两位前面补零)。 在Delphi中,我们可以采用以下步骤: 1. 遍历字符串中的每个字符(注意:Delphi的字符串是Unicode字符串,每个字符是WideChar,即两个字节)。 2. 将每个字符换为整数(使用Ord函数)。 3. 将整数换为十六进制字符串(使用IntToHex函数),并确保两位(对于单个字节)或四位(对于两个字节)?但是注意,汉字在Unicode中通常需要4个十六进制数字(即两个字节)。不过,IntToHex可以指定位数,我们可以指定4位,但是注意:对于ASCII字符(0-255)我们通常用两位表示,但Unicode字符需要四位? 但是,根据引用[2]中的C#代码,它是将每个字符换为两位十六进制(对于ASCII字符)?实际上,在C#代码中,他们使用了"{0:X2}",这表示两位十六进制,但这里有一个问题:对于Unicode字符(如汉字),一个字符是两字节,所以应该换为4个十六进制数字。然而,在C#代码中,他们使用Convert.ToInt32(chr)得到的是字符的Unicode码点(一个整数),然后格式化为两位十六进制,这里实际上只取了一个字节?这显然是错误的,因为一个Unicode字符可能超出单字节范围。 实际上,正确的做法应该是: - 在C#中,Convert.ToInt32(chr)返回的是字符的Unicode码点(例如,'A'是65,'你'是20320),然后使用格式化为X2(两位)是不对的,因为汉字需要4位(如20320的十六进制是4F60,需要4位)。所以引用[2]中的方法ConvertStringToHex使用了"{0:X2}",这实际上只输出两个十六进制数字,会丢失高位字节,导致错误。 因此,我们在Delphi中应该将每个字符(WideChar)换为4位的十六进制字符串(因为每个字符是两字节)。或者,我们可以将字符串换为字节数组(使用TEncoding.Unicode.GetBytes),然后将每个字节换为两位十六进制。 两种方法: 方法1:逐个字符处理,每个字符换为4位十六进制。 优点:简单直接。 缺点:如果字符串中包含代理对(Surrogate Pair)表示的字符(即两个WideChar表示一个字符),这种方法会将其当作两个字符处理,从而产生8位十六进制数字,但实际上它应该是一个字符(4字节)。不过,对于常见汉字(在BMP平面内)没有问题。 方法2:将整个字符串换为字节数组(使用UTF-16编码),然后将字节数组中的每个字节换为两位十六进制。 优点:处理简单,且对于代理对也能正确换(因为代理对在UTF-16编码中就是四个字节表示一个字符)。 缺点:生成的十六进制字符串是字节序列的顺序,注意字节序(小端序还是大端序)?在Delphi中,默认是使用小端序(低字节在前)。 考虑到通用性,我们使用方法2,因为它能处理任何字符(包括代理对)。 步骤: 1. 使用TEncoding.Unicode.GetBytes将字符串换为字节数组(Byte array)。 2. 遍历字节数组中的每个字节,将其换为两位十六进制字符串。 3. 将所有的两位十六进制字符串连接起来。 注意:字节数组的字节顺序(Endianness)由TEncoding.Unicode决定,在Windows系统上通常是Little Endian(小端序)。 函数实现: ```pascal function StringToHex(const S: string; Separator: string = ''): string; var Bytes: TBytes; I: Integer; begin // 将字符串换为UTF-16编码的字节数组(小端序) Bytes := TEncoding.Unicode.GetBytes(S); Result := ''; for I := 0 to Length(Bytes) - 1 do begin // 将每个字节换为两位十六进制,并追加分隔符(如果指定) Result := Result + IntToHex(Bytes[I], 2) + Separator; end; // 如果指定了分隔符,最后会多一个分隔符,去掉它 if (Separator <> '') and (Result <> '') then Delete(Result, Length(Result) - Length(Separator) + 1, Length(Separator)); end; ``` 这个函数还支持可选的分隔符(如空格、冒号等),默认没有分隔符。 示例: 输入:StringToHex('你') -> 返回'604F' (注意:小端序,所以'4F60'在内存中是60,4F,所以输出为604F) 但是,我们之前十六进制汉字时,输入的格式是'4F60'(大端序?),而这里输出的是小端序(604F)。这会导致不一致吗? 实际上,在换回字符串时,我们使用同样的字节序(小端序)即可。因为之前的HexToHanzi函数也是使用TEncoding.Unicode(小端序)来解码的。所以,如果我们用StringToHex换得到的十六进制字符串,再用HexToHanzi回来,应该是没有问题的。 但是,如果我们需要大端序的输出(即按照正常书写顺序,高位在前),那么我们可以调整字节数组的顺序(交换每两个字节的顺序)?或者使用TEncoding.BigEndianUnicode。不过,在Windows环境中,通常使用小端序。 为了与之前十六进制汉字函数保持一致(之前的函数也是按小端序解析的),我们这里使用TEncoding.Unicode(小端序)是合理的。 注意:如果用户需要大端序,我们可以提供另一种选择。但这里我们先按照小端序实现。 另外,上面的函数在添加分隔符后,最后会多一个分隔符,所以在循环后需要删除最后多余的分隔符。 优化:对于大字符串,使用TStringBuilder来提升性能。 优化后的版本: ```pascal function StringToHex(const S: string; Separator: string = ''): string; var Bytes: TBytes; I: Integer; SB: TStringBuilder; begin Bytes := TEncoding.Unicode.GetBytes(S); SB := TStringBuilder.Create; try for I := 0 to Length(Bytes) - 1 do begin SB.Append(IntToHex(Bytes[I], 2)); if (Separator <> '') and (I < Length(Bytes) - 1) then SB.Append(Separator); end; Result := SB.ToString; finally SB.Free; end; end; ``` 这个版本避免了最后多余的分隔符,而且使用TStringBuilder效率更高。 示例: 调用 StringToHex('A') 将返回 '4100' (因为'A'的Unicode编码是$0041,小端序存储为41,00) 调用 StringToHex('A', ' ') 将返回 '41 00' 注意:字母'A'在Unicode中也是两个字节(小端序:41 00)。如果只想换ASCII部分,可以改用单字节编码(如TEncoding.ASCII),但这样就不能处理汉字了。 如果用户需要将汉字换为四位十六进制(大端序表示),我们可以按字符处理,而不是按字节: ```pascal function StringToHexByChar(const S: string; Separator: string = ''): string; var I: Integer; Code: Integer; SB: TStringBuilder; begin SB := TStringBuilder.Create; try for I := 1 to Length(S) do begin Code := Ord(S[I]); // 得到Unicode码点(整数) SB.Append(IntToHex(Code, 4)); if (Separator <> '') and (I < Length(S)) then SB.Append(Separator); end; Result := SB.ToString; finally SB.Free; end; end; ``` 这个函数将每个字符换为4位的十六进制字符串(高位在前),例如:'你'(20320)换为'4F60'。并且支持分隔符。 两种方式的选择: - 第一种(按字节):输出的是原始字节序列(小端序),每个字符对应4位十六进制(两个字节,但顺序是反的),用于存储或传输。 - 第二种(按字符):输出的是每个字符的码点(大端序表示),更符合阅读习惯。 用户可以根据需要选择。但注意,如果字符串中包含代理对(如一些特殊字符),第二种方法会输出两个码点(代理对的两个字符的码点),而不是一个完整的字符的码点。而第一种方法则会将代理对表示为四个字节(小端序),可以正确回来。 因此,为了通用性,我们通常推荐第一种方法(按字节数组换)。如果确定字符串只包含BMP平面的字符(包括常用汉字),且需要可读性(大端序),则可以用第二种。 现在,根据用户之前的问题(十六进制汉字),我们使用的是按字节数组的方法(小端序),所以这里我们也按字节数组实现StringToHex,以保持一致性。 因此,我们提供第一种方法(按字节数组,小端序)的实现。 总结: 在Delphi中,将字符串(包括汉字)换为十六进制字符串(小端序,UTF-16编码)的方法如下: ```pascal function StringToHex(const S: string; Separator: string = ''): string; var Bytes: TBytes; I: Integer; SB: TStringBuilder; begin Bytes := TEncoding.Unicode.GetBytes(S); SB := TStringBuilder.Create; try for I := 0 to High(Bytes) do begin SB.Append(IntToHex(Bytes[I], 2)); if (Separator <> '') and (I < High(Bytes)) then SB.Append(Separator); end; Result := SB.ToString; finally SB.Free; end; end; ``` 注意:High(Bytes)返回的是数组最大下标(Length(Bytes)-1)。 使用示例: StringToHex('A') 返回 '4100' StringToHex('你') 返回 '604F' (因为汉字“你”的Unicode码点是4F60,小端序存储为60,4F) 如果用户需要大写字母的十六进制,可以将IntToHex(Bytes[I], 2)改为IntToHex(Bytes[I], 2).ToUpper,但IntToHex默认返回的是大写。 最后,生成相关问题。 </think>以下是Delphi中将字符串(String)换为十六进制(Hex)的完整实现方法,适用于包含汉字的Unicode字符串: ### 核心原理 1. **Unicode处理**:Delphi使用UTF-16编码存储字符串,每个字符占2字节(包括汉字)[^1] 2. **换步骤**: - 字符串 → UTF-16字节数组 → 每个字节为2位十六进制 3. **字节序**:Windows系统默认使用小端序(Little-Endian),如汉字"你"(U+4F60)存储为`60 4F` ### 完整代码实现 ```pascal function StringToHex(const Input: string; Separator: string = ''): string; var Bytes: TBytes; I: Integer; SB: TStringBuilder; begin // 字符串UTF-16字节数组 Bytes := TEncoding.Unicode.GetBytes(Input); SB := TStringBuilder.Create; try for I := 0 to High(Bytes) do begin // 每个字节为2位十六进制 SB.Append(IntToHex(Bytes[I], 2)); // 添加可选分隔符(非末尾元素) if (Separator <> '') and (I < High(Bytes)) then SB.Append(Separator); end; Result := SB.ToString; finally SB.Free; end; end; ``` ### 使用示例 ```pascal // 基础换 ShowMessage(StringToHex('Hello')); // 输出: 480065006C006C006F00 ShowMessage(StringToHex('你好')); // 输出: 604F7D59 // 带空格分隔符 ShowMessage(StringToHex('A', ' ')); // 输出: 41 00 // 带冒号分隔的汉字 ShowMessage(StringToHex('测试', ':')); // 输出: B84E:8765 ``` ### 关键说明 1. **编码处理**: - `TEncoding.Unicode` 处理UTF-16编码 - `IntToHex(..., 2)` 确保单字节总输出2位十六进制(如`0A`) 2. **分隔符优化**: - 可选参数`Separator`支持自定义分隔符(空格/冒号等) - 自动跳过末尾多余的分隔符 3. **性能优化**: - 使用`TStringBuilder`避免频繁内存分配[^2] - 时间复杂度:$O(n)$,$n$为字节数组长度 ### 验证反向换 ```pascal // 十六进制回字符串(使用之前实现的HexToHanzi函数) var Original, HexStr: string; begin Original := '汉字测试'; HexStr := StringToHex(Original); ShowMessage(HexToHanzi(HexStr)); // 输出: "汉字测试" end; ``` ### 常见问题 1. **字节序问题**: 小端序输出`604F`表示"你"(U+4F60),如需大端序可调整字节顺序: ```pascal // 大端序换(交换字节位置) function SwapBytes(Value: Word): Word; begin Result := (Value shl 8) or (Value shr 8); end; ``` 2. **特殊字符处理**: 代理对字符(如😊)会被自动正确处理为4字节编码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值