代码之家  ›  专栏  ›  技术社区  ›  Reed Olsen

如何将字母数字电话号码转换为数字

  •  6
  • Reed Olsen  · 技术社区  · 15 年前

    更新:

    我的实用程序的最终版本如下:

    StringBuilder b = new StringBuilder();
    
    for(char c : inLetters.toLowerCase().toCharArray())
    {
        switch(c)
        {
        case '0':                                          b.append("0"); break;
        case '1':                                          b.append("1"); break;
        case '2': case 'a': case 'b': case 'c':            b.append("2"); break;
        case '3': case 'd': case 'e': case 'f':            b.append("3"); break;
        case '4': case 'g': case 'h': case 'i':            b.append("4"); break;
        case '5': case 'j': case 'k': case 'l':            b.append("5"); break;
        case '6': case 'm': case 'n': case 'o':            b.append("6"); break;
        case '7': case 'p': case 'q': case 'r': case 's':  b.append("7"); break;
        case '8': case 't': case 'u': case 'v':            b.append("8"); break;
        case '9': case 'w': case 'x': case 'y': case 'z':  b.append("9"); break;
        }
    }
    
    return builder.toString();
    

    原始问题:

    我正在承担将字母数字电话号码转换为一串数字的简单任务。例如,1-800-hi-haxor将变为1-800-44-42967。我最初的尝试是创建一个讨厌的switch语句,但我希望有一个更优雅、更高效的解决方案。以下是我的资料:

    for(char c : inLetters.toLowerCase().toCharArray())
    {
        switch(c)
        {
        case '0':                                         result+="0"; break;
        case '1':                                         result+="1"; break;
        case '2': case 'a': case 'b': case 'c':           result+="2"; break;
        case '3': case 'd': case 'e': case 'f':           result+="3"; break;
        case '4': case 'g': case 'h': case 'i':           result+="4"; break;
        case '5': case 'j': case 'k': case 'l':           result+="5"; break;
        case '6': case 'm': case 'n': case 'o':           result+="6"; break;
        case '7': case 'p': case 'q': case 'r': case 's': result+="7"; break;
        case '8': case 't': case 'u': case 'v':           result+="8"; break;
        case '9': case 'w': case 'x': case 'y': case 'z': result+="9"; break;
        }
    }
    

    谢谢!

    7 回复  |  直到 12 年前
        1
  •  9
  •   Quinn Taylor    15 年前

    switch语句并没有那么糟糕。您的算法与电话号码的长度是线性的。代码可读,并且很容易通过检查进行验证。我不会把它弄乱的,除了增加一个 default 处理错误的案例。(我不是Java程序员,所以请原谅我,如果它被称为其他东西。)

    如果你 为了加快速度,预先初始化的按字符索引的表将避免基本错误检查之外的任何比较。您甚至可以通过复制表中的值来避免大小写转换。( digit['A'] = digit['a'] = "2"; )初始化表的成本将在转换总数上分摊。

        2
  •  6
  •   grkvlt    15 年前

    您可以使用ApacheCommonsLangStringUtils执行此操作,如下所示:

    String output = StringUtils.replaceChars(StringUtils.lowerCase(input),
                        "abcdefghijklmnopqrstuvwxyz",
                        "22233344455566677778889999");
    

    当然,假设速度不是您主要关心的问题,并且您需要一个紧凑的解决方案;)

        3
  •  5
  •   erickson    15 年前

    使用A Map ,其中键是字母和数字,值是键盘上的数字。(因此,每个小键盘编号都将由三个或四个字母和一个数字组成索引)。

    Map<Character, Character> keypad = new HashMap<Character, Character>();
    ...
    StringBuilder buf = new StringBuilder(inLetters.length());
    for (int idx = 0; idx < inLetters.length(); ++idx) {
      Character ch = keypad.get(inLetters.charAt(idx));
      if (ch != null)
        buf.append(ch);
    }
    

    更新: 我很好奇一个手工编码的查找表是否比一个密集的查找表性能更好 switch 案例。在我的非正式测试中,我发现以下代码是我能想到的最快的代码:

      private static final char[] lut = 
        "0123456789:;<=>?@22233344455566677778889999[\\]^_`22233344455566677778889999".toCharArray();
    
      private static final char min = lut[0];
    
      String fastest(String letters)
      {
        int n = letters.length();
        char[] buf = new char[n];
        while (n-- > 0) {
          int ch = letters.charAt(n) - min;
          buf[n] = ((ch < 0) || (ch >= lut.length)) ? letters.charAt(n) : lut[ch];
        }
        return new String(buf);
      }
    

    令人惊讶的是,使用switch语句(编译为 tableswitch 说明)。这只是为了好玩,提醒你,但是在我的笔记本电脑上,在一个线程中运行,我可以在大约1.3秒内转换1000万个10字母-“数字”。我真的很惊讶,因为据我所知, 桌上女巫 基本上以相同的方式操作,但我希望它更快,因为它是一个JVM指令。

    当然,除非我只为我能转换的无限数量的电话号码中的每一个获得报酬,否则我绝不会写这样的代码。交换机的可读性更高,性能也很好,而且在未来的一些JVM中可能会获得免费的性能提升。

    远远地说,对原始代码的最大改进是使用 StringBuilder 而不是连接字符串,这不会损害代码的可读性。使用 charAt 而不是将输入转换为 char[] 也使代码更简单,更容易理解 也提高了性能。最后,附加 char 文字而不是 String 文字量(文字) '1' 而不是 "1" )性能的提高也有助于提高可读性。

        4
  •  4
  •   JRL    15 年前

    简单地说:

       String convert(String inLetters) {
          String digits = "22233344455566677778889999";
          String alphas = "abcdefghijklmnopqrstuvwxyz";
          String result = "";
          for (char c : inLetters.toLowerCase().toCharArray()) {
              int pos = alphas.indexOf(c);
              result += (pos == -1 ? c : digits.charAt(pos));
          }
          return result;
       }
    
        5
  •  2
  •   RMorrisey    15 年前

    如果你想要一个不强迫你列举所有字母的解决方案,你可以做如下的事情:

    char convertedChar = c;
    if (Character.isLetter(c)) {
        //lowercase alphabet ASCII codes: 97 (a)-122 (z)
        int charIndex = ((int)c) - 97;
        //make adjustments to account for 's' and 'z'
        if (charIndex >= 115) { //'s'
            charIndex--;
        }
        if (charIndex == 121) { //'z'-1
            charIndex--;
        }
        convertedChar = (char)(2 + (charIndex/3));
    }
    result += convertedChar;
    
        6
  •  1
  •   Mike Dunlavey    15 年前

    如果您在一个紧密的循环中运行10^9次,并按ctrl键将其中断几次,我敢打赌,几乎每一次它都会深入到字符串类中,试图完成那些看起来无辜的“+=”运算符之一。

        7
  •  0
  •   Quinn Taylor    15 年前

    switch语句被编译成类似于if else语句的形式,(每个 case 语句本质上是一个 if (c == '...') 伪装测试)因此,尽管这比级联测试在视觉上更紧凑,如果是针对每个字符的测试,那么可能有或没有任何真正的性能优势。

    您可以通过消除一些比较来简化它。关键是 char 是整数类型(这就是为什么可以打开 烧焦 )所以可以使用数值比较运算符。然后“A”给你的 inLetters 字符串只包含字母数字字符,这应该有效…(所有其他字符将通过未更改的字符。)

    String result = "";
    for (char c : letters.toLowerCase().toCharArray()) {
        if      (c <= '9') result += c;
        else if (c <= 'c') result += "2";
        else if (c <= 'f') result += "3";
        else if (c <= 'i') result += "4";
        else if (c <= 'l') result += "5";
        else if (c <= 'o') result += "6";
        else if (c <= 's') result += "7";
        else if (c <= 'v') result += "8";
        else if (c <= 'z') result += "9";
        else               result += c;
    }
    

    感兴趣的字符具有十六进制值:“0”=0x30、“9”=0x39、“A”=0x61和“Z”=0x7A。

    编辑: 最好使用 StringBuilder append() 创建字符串,但对于小字符串,它不太可能快得多。( Amdahl's Law 演示了优化代码所能期望的实际加速速度受实际花费在该代码中的时间百分比的限制。)我只使用连接字符串使算法对OP清晰可见。