【问题标题】:'Reliable' SMS Unicode & GSM Encoding in PHPPHP 中的“可靠”SMS Unicode 和 GSM 编码
【发布时间】:2010-09-06 20:32:43
【问题描述】:

更新了一点

必须说,我对使用 PHP 进行国际化不是很有经验,而且大量搜索并没有真正提供我正在寻找的答案。

我需要找到一种可靠的方法来仅将“相关”文本转换为 Unicode,以便使用 PHP(只是暂时的,而服务是使用 C# 重写的)在 SMS 消息中发送 - 显然,在时刻以纯文本形式发送。

我可以想象将所有内容都转换为 Unicode 字符集(而不是使用标准 GSM 字符集),但这意味着 所有 消息将被限制为 70 个字符(而不是 160 个)。

所以,我想我真正的问题是:检测消息是否需要 Unicode 编码的最可靠方法是什么,所以我只需要在它是 绝对必要 (例如对于非拉丁语言字符)?

添加信息:

好的,所以我花了一个上午的时间来处理这个问题,但我仍然没有比开始时更进一步(当然是因为我在字符集转换方面完全缺乏能力)。所以这是修改后的场景:

我有来自外部来源的短信,这个外部来源以纯文本 + 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 提供商发送。显然,将上述内容作为纯文本发送会导致很多缺失的字符(它们被我的提供者替换为空格) - 我需要采用与存在的内容相关的内容。我想如下:

  1. 如果所有文本都在 GSM 03.38 codepage 内,请按原样发送。 (除上述希伯来字符外,所有字符都属于这一类,但需要转换。)

  2. 否则,请将其转换为 Unicode,然后通过多条消息发送(因为 Unicode 限制为 70 个字符,而不是 SMS 的 160 个字符)。

正如我上面所说,我很难在 PHP 中执行此操作(由于内置了一些简单的转换函数,C# 并不是什么大问题),但很可能我只是错过了明显的,这里.我在 PHP 中也找不到任何用于 7 位编码的预制转换类 - 我尝试自己转换字符串并发送它似乎是徒劳的。

我们将不胜感激。

【问题讨论】:

    标签: php unicode sms gsm


    【解决方案1】:

    在进入机制之前从概念上处理它,如果有任何明显的地方道歉,可以将字符串定义为 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。

    您需要在 utf8 中的 php 字符串和代码点序列之间进行转换的函数可以在 http://hsivonen.iki.fi/php-utf8/ 找到,这就是您的 codepoints() 函数。

    如果您从提供 Unicode 斜线转义字符(“让我们测试 \u00f6\u00e4\u00fc...”)的外部来源获取数据,则该字符串转义格式应转换为 utf8。我不知道执行此操作的函数,如果找不到,这是字符串/正则表达式处理+使用 hsivonen.iki.fi 函数的问题,例如当你点击 \u00f6 时,替换它使用代码点 0xf6 的 utf8 表示。

    【讨论】:

      【解决方案2】:

      虽然这是一个旧线程,但我最近不得不解决一个非常相似的问题并想发布我的答案。 PHP 代码有些简单。它从数组中的一个非常大的 GSM 有效字符代码数组开始,然后使用 ord($string) function 简单地检查当前字符是否在该数组中,它返回传递的字符串的第一个字符的 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;
      

      【讨论】:

      • 我看不到提供的代码在哪里使用ord() !?
      【解决方案3】:
      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;
      }
      

      【讨论】:

        【解决方案4】:

        我知道这不是 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;
        }
        

        【讨论】:

          【解决方案5】:

          PHP6 将有更好的 unicode 支持,但您可以使用一些函数。

          我的第一个想法是mb_convert_encoding,但正如你所说,这会将消息缩短到 70 个字符 - 所以也许你可以将它与 mb_detect_encoding 结合使用?

          见:Multibyte Functions

          【讨论】:

            【解决方案6】:
            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)
            

            【讨论】:

              猜你喜欢
              • 2012-01-25
              • 2018-06-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多