PHP 中“可靠”的 SMS Unicode 和 GSM 编码
题
(更新了一点)
必须说,我对使用 PHP 进行国际化并不是很有经验,并且大量搜索并没有真正提供我正在寻找的答案。
我需要找到一种可靠的方法,使用 PHP 仅将“相关”文本转换为 Unicode 以在 SMS 消息中发送(只是暂时的,同时使用 C# 重写服务) - 显然,当前发送的消息已发送作为纯文本。
我可以想象将所有内容转换为 Unicode 字符集(而不是使用标准 GSM 字符集),但这意味着 全部 消息长度将限制为 70 个字符(而不是 160 个)。
所以,我想我真正的问题是: 检测消息是否需要 Unicode 编码的最可靠方法是什么,所以我只需要在它满足时才这样做 绝对有必要 (例如。对于非拉丁语言字符)?
添加信息:
好吧,我花了一个早上的时间来解决这个问题,但与开始时相比仍然没有进展(当然是因为我在字符集转换方面完全缺乏能力)。这是修改后的场景:
我有来自外部源的文本 SMS 消息,该外部源以纯文本 + Unicode 斜杠转义字符提供回复。例如。“显示”文本:
让我们来测试一下
返回:
让我们测试一下 \u00f6\u00e4\u00fc \u00e9\u00e0\u00e8 \u05d0\u05d9\u05df \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea
现在,我可以以纯文本、GSM 03.38 或 Unicode 发送到我的 SMS 提供商。显然,以纯文本形式发送上述内容会导致大量丢失字符(它们被我的提供商替换为空格) - 我需要采用与存在的内容相关的内容。我想要什么 做 如下:
如果所有文本都在 GSM 03.38 代码页, ,按原样发送。(除上述希伯来字符外,所有字符都属于此类别,但需要转换。)
否则,将其转换为 Unicode,并通过多条消息发送(因为 Unicode 限制是 70 个字符,而不是 SMS 的 160 个字符)。
正如我上面所说,我对在 PHP 中执行此操作感到困惑(由于内置了一些简单的转换函数,C# 并不是什么大问题),但很可能我只是在这里错过了显而易见的事情。我也无法在 PHP 中找到任何用于 7 位编码的预制转换类 - 而且我尝试自己转换字符串并将其发送似乎是徒劳的。
任何帮助将不胜感激。
解决方案
为了在进入机制之前从概念上处理它,并且如果其中任何一个是明显的,我们深表歉意,字符串可以定义为 Unicode 字符序列,Unicode 是一个数据库,它为您可能遇到的每个字符提供一个称为代码点的 id 号。需要合作。GSM-338 包含 Unicode 字符的子集,因此您要做的就是从字符串中提取一组代码点,并检查该组代码点是否包含在 GSM-338 中。
// second column of http://unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT
$gsm338_codepoints = array(0x0040, 0x0000, ..., 0x00fc, 0x00e0)
$can_use_gsm338 = true;
foreach(codepoints($mystring) as $codepoint){
if(!in_array($codepoint, $gsm338_codepoints)){
$can_use_gsm338 = false;
break;
}
}
这就留下了函数 codepoints($string) 的定义,它不是 PHP 内置的。PHP 将字符串理解为字节序列而不是 Unicode 字符序列。弥补差距的最佳方法是尽快将字符串转换为 UTF8,并尽可能长时间地保留它们 - 在处理外部系统时,您将不得不使用其他编码,但要隔离到与该系统的接口并且仅在内部处理 utf8。
您需要在 utf8 中的 php 字符串和代码点序列之间进行转换的函数可以在以下位置找到: http://hsivonen.iki.fi/php-utf8/ ,这就是您的 codepoints() 函数。
如果您从提供 Unicode 斜杠转义字符的外部源获取数据(“让我们测试 \u00f6\u00e4\u00fc...”),则该字符串转义格式应转换为 utf8。我不知道有什么函数可以做到这一点,如果找不到,那就是字符串/正则表达式处理+使用 hsivonen.iki.fi 函数的问题,例如,当您点击 \u00f6 时,替换它使用代码点 0xf6 的 utf8 表示形式。
其他提示
虽然这是一个旧线程,但我最近不得不解决一个非常相似的问题,并想发布我的答案。PHP 代码有些简单。它从一个数组中的一个非常大的 GSM 有效字符代码数组开始,然后使用以下命令简单地检查当前字符是否在该数组中: ord($string) 函数 它返回传递的字符串的第一个字符的 ascii 值。下面是我用来验证字符串是否具有 GSM 价值的代码。
$valid_gsm_keycodes = Array(
0x0040, 0x0394, 0x0020, 0x0030, 0x00a1, 0x0050, 0x00bf, 0x0070,
0x00a3, 0x005f, 0x0021, 0x0031, 0x0041, 0x0051, 0x0061, 0x0071,
0x0024, 0x03a6, 0x0022, 0x0032, 0x0042, 0x0052, 0x0062, 0x0072,
0x00a5, 0x0393, 0x0023, 0x0033, 0x0043, 0x0053, 0x0063, 0x0073,
0x00e8, 0x039b, 0x00a4, 0x0034, 0x0035, 0x0044, 0x0054, 0x0064, 0x0074,
0x00e9, 0x03a9, 0x0025, 0x0045, 0x0045, 0x0055, 0x0065, 0x0075,
0x00f9, 0x03a0, 0x0026, 0x0036, 0x0046, 0x0056, 0x0066, 0x0076,
0x00ec, 0x03a8, 0x0027, 0x0037, 0x0047, 0x0057, 0x0067, 0x0077,
0x00f2, 0x03a3, 0x0028, 0x0038, 0x0048, 0x0058, 0x0068, 0x0078,
0x00c7, 0x0398, 0x0029, 0x0039, 0x0049, 0x0059, 0x0069, 0x0079,
0x000a, 0x039e, 0x002a, 0x003a, 0x004a, 0x005a, 0x006a, 0x007a,
0x00d8, 0x001b, 0x002b, 0x003b, 0x004b, 0x00c4, 0x006b, 0x00e4,
0x00f8, 0x00c6, 0x002c, 0x003c, 0x004c, 0x00d6, 0x006c, 0x00f6,
0x000d, 0x00e6, 0x002d, 0x003d, 0x004d, 0x00d1, 0x006d, 0x00f1,
0x00c5, 0x00df, 0x002e, 0x003e, 0x004e, 0x00dc, 0x006e, 0x00fc,
0x00e5, 0x00c9, 0x002f, 0x003f, 0x004f, 0x00a7, 0x006f, 0x00e0 );
for($i = 0; $i < strlen($string); $i++) {
if(!in_array($string[$i], $valid_gsm_keycodes)) return false;
}
return true;
function is_gsm0338( $utf8_string ) {
$gsm0338 = array(
'@','Δ',' ','0','¡','P','¿','p',
'£','_','!','1','A','Q','a','q',
'$','Φ','"','2','B','R','b','r',
'¥','Γ','#','3','C','S','c','s',
'è','Λ','¤','4','D','T','d','t',
'é','Ω','%','5','E','U','e','u',
'ù','Π','&','6','F','V','f','v',
'ì','Ψ','\'','7','G','W','g','w',
'ò','Σ','(','8','H','X','h','x',
'Ç','Θ',')','9','I','Y','i','y',
"\n",'Ξ','*',':','J','Z','j','z',
'Ø',"\x1B",'+',';','K','Ä','k','ä',
'ø','Æ',',','<','L','Ö','l','ö',
"\r",'æ','-','=','M','Ñ','m','ñ',
'Å','ß','.','>','N','Ü','n','ü',
'å','É','/','?','O','§','o','à'
);
$len = mb_strlen( $utf8_string, 'UTF-8');
for( $i=0; $i < $len; $i++)
if (!in_array(mb_substr($utf8_string,$i,1,'UTF-8'), $gsm0338))
return false;
return true;
}
我知道这不是 php 代码,但我认为无论如何它可能会有所帮助。这就是我在我编写的一个应用程序中执行此操作的方法,该应用程序用于检测是否可以作为 GSM 03.38 发送(您可以对纯文本执行类似的操作)。它有两个转换表,一个用于普通 GSM,另一个用于扩展。然后是一个循环遍历所有字符检查是否可以转换的函数。
#define UCS2_TO_GSM_LOOKUP_TABLE_SIZE 0x100
#define NON_GSM 0x80
#define UCS2_GCL_RANGE 24
#define UCS2_GREEK_CAPITAL_LETTER_ALPHA 0x0391
#define EXTEND 0x001B
// note that the ` character is mapped to ' so that all characters that can be typed on
// a standard north american keyboard can be converted to the GSM default character set
static unsigned char Ucs2ToGsm[UCS2_TO_GSM_LOOKUP_TABLE_SIZE] =
{ /*+0x0 +0x1 +0x2 +0x3 +0x4 +0x5 +0x6 +0x7*/
/*0x00*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x08*/ NON_GSM, NON_GSM, 0x0a, NON_GSM, NON_GSM, 0x0d, NON_GSM, NON_GSM,
/*0x10*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x18*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x20*/ 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27,
/*0x28*/ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
/*0x30*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/*0x38*/ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
/*0x40*/ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
/*0x48*/ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
/*0x50*/ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
/*0x58*/ 0x58, 0x59, 0x5a, EXTEND, EXTEND, EXTEND, EXTEND, 0x11,
/*0x60*/ 0x27, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/*0x68*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
/*0x70*/ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
/*0x78*/ 0x78, 0x79, 0x7a, EXTEND, EXTEND, EXTEND, EXTEND, NON_GSM,
/*0x80*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x88*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x90*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0x98*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0xa0*/ NON_GSM, 0x40, NON_GSM, 0x01, 0x24, 0x03, NON_GSM, 0x5f,
/*0xa8*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0xb0*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM,
/*0xb8*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, 0x60,
/*0xc0*/ NON_GSM, NON_GSM, NON_GSM, NON_GSM, 0x5b, 0x0e, 0x1c, 0x09,
/*0xc8*/ NON_GSM, 0x1f, NON_GSM, NON_GSM, NON_GSM, NON_GSM, NON_GSM, 0x60,
/*0xd0*/ NON_GSM, 0x5d, NON_GSM, NON_GSM, NON_GSM, NON_GSM, 0x5c, NON_GSM,
/*0xd8*/ 0x0b, NON_GSM, NON_GSM, NON_GSM, 0x5e, NON_GSM, NON_GSM, 0x1e,
/*0xe0*/ 0x7f, NON_GSM, NON_GSM, NON_GSM, 0x7b, 0x0f, 0x1d, NON_GSM,
/*0xe8*/ 0x04, 0x05, NON_GSM, NON_GSM, 0x07, NON_GSM, NON_GSM, NON_GSM,
/*0xf0*/ NON_GSM, 0x7d, 0x08, NON_GSM, NON_GSM, NON_GSM, 0x7c, NON_GSM,
/*0xf8*/ 0x0c, 0x06, NON_GSM, NON_GSM, 0x7e, NON_GSM, NON_GSM, NON_GSM
};
static unsigned char Ucs2GclToGsm[UCS2_GCL_RANGE + 1] =
{
/*0x0391*/ 0x41, // Alpha A
/*0x0392*/ 0x42, // Beta B
/*0x0393*/ 0x13, // Gamma
/*0x0394*/ 0x10, // Delta
/*0x0395*/ 0x45, // Epsilon E
/*0x0396*/ 0x5A, // Zeta Z
/*0x0397*/ 0x48, // Eta H
/*0x0398*/ 0x19, // Theta
/*0x0399*/ 0x49, // Iota I
/*0x039a*/ 0x4B, // Kappa K
/*0x039b*/ 0x14, // Lambda
/*0x039c*/ 0x4D, // Mu M
/*0x039d*/ 0x4E, // Nu N
/*0x039e*/ 0x1A, // Xi
/*0x039f*/ 0x4F, // Omicron O
/*0x03a0*/ 0X16, // Pi
/*0x03a1*/ 0x50, // Rho P
/*0x03a2*/ NON_GSM,
/*0x03a3*/ 0x18, // Sigma
/*0x03a4*/ 0x54, // Tau T
/*0x03a5*/ 0x59, // Upsilon Y
/*0x03a6*/ 0x12, // Phi
/*0x03a7*/ 0x58, // Chi X
/*0x03a8*/ 0x17, // Psi
/*0x03a9*/ 0x15 // Omega
};
bool Gsm0338Encoding::IsNotGSM( wchar_t szUnicodeChar )
{
bool result = true;
if( szUnicodeChar < UCS2_TO_GSM_LOOKUP_TABLE_SIZE )
{
result = ( Ucs2ToGsm[szUnicodeChar] == NON_GSM );
}
else if( (szUnicodeChar >= UCS2_GREEK_CAPITAL_LETTER_ALPHA) &&
(szUnicodeChar <= (UCS2_GREEK_CAPITAL_LETTER_ALPHA + UCS2_GCL_RANGE)) )
{
result = ( Ucs2GclToGsm[szUnicodeChar - UCS2_GREEK_CAPITAL_LETTER_ALPHA] == NON_GSM );
}
else if( szUnicodeChar == 0x20AC ) // €
{
result = false;
}
return result;
}
bool Gsm0338Encoding::IsGSM( const std::wstring& str )
{
bool result = true;
if( std::find_if( str.begin(), str.end(), IsNotGSM ) != str.end() )
{
result = false;
}
return result;
}
PHP6 将拥有更好的 unicode 支持,但您可以使用一些函数。
我的第一个想法是 mb_convert_encoding
但正如你所说,这会将消息缩短到 70 个字符 - 所以也许你可以将其与 mb_detect_encoding
?
看: 多字节函数
preg_match('/^[\x0A\x0C\x0D\x20-\x5F\x61-\x7E\xA0\xA1\xA3-\xA5\xA7'.
'\xBF\xC4-\xC6\xC9\xD1\xD6\xD8\xDC\xDF\xE0\xE4-\xE9\xEC\xF1'.
'\xF2\xF6\xF8\xF9\xFC'.
json_decode('"\u0393\u0394\u0398\u039B\u039E\u03A0\u03A3\u03A6\u03A8\u03A9\u20AC"').
']*$/u', $text)
或者
preg_match('/^[\x0A\x0C\x0D\x20-\x5F\x61-\x7E\xA0\xA1\xA3-\xA5\xA7\xBF\xC4-\xC6\xC9\xD1\xD6\xD8\xDC\xDF\xE0\xE4-\xE9\xEC\xF1\xF2\xF6\xF8\xF9\xFCΓΔΘΛΞΠΣΦΨΩ€]*$/u', $text)