代码之家  ›  专栏  ›  技术社区  ›  Morinar

需要更好的方法在C中格式化电话号码吗

  •  2
  • Morinar  · 技术社区  · 17 年前

    我有一个字符数组,其中包含一个电话号码的形式:“(xxx)xxx xxxx xxxx”,并需要将其转换为形式:“xxx xxx xxxx”,在这里我只需要截断分机。我在函数中的初始过程如下所示:

    static void formatPhoneNum( char *phoneNum ) {
        unsigned int i;
        int numNumbers = 0;
        /* Change the closing parenthesis to a dash and truncate at 12 chars. */
        for ( i = 0; i < strlen( phoneNum ); i++ ) {
            if ( phoneNum[i] == ')' ) {
                phoneNum[i] = '-';
            }
            else if ( i == 13 ) {
                phoneNum[i] = '\0';
                break;
            }
            else if ( isdigit( phoneNum[i] ) ) {
                numNumbers++;
            }
        }
    
        /* If the phone number is empty or not a full phone number, 
         * i.e. just parentheses and dashes, or not 10 numbers
         * format it as an emtpy string. */
        if ( numNumbers != 10 ) {
            strcpy( phoneNum, "" );
        }
        else {
            /* Remove the first parenthesis. */
            strcpy( phoneNum, phoneNum + 1 );
        }
    }
    

    我删除前导参数的方式让人感觉有点笨拙,但我不能只是在函数中增加指针,因为调用版本的指针不会得到更新。我也觉得我可以在整个功能中“更聪明”。

    有什么想法/建议吗?

    6 回复  |  直到 17 年前
        1
  •  6
  •   mocj    17 年前

    既然您声明您的输入保证采用正确的格式,那么以下内容如何:

    static void formatPhoneNum( char *phoneNum )
    {
        memmove(phoneNum, phoneNum + 1, 12);
        phoneNum[3]  = '-';
        phoneNum[12] = 0;
    }
    

        2
  •  2
  •   PTBNL    17 年前

    正如Pavel所说,你不能把一根绳子粘在自己身上。为了清晰起见,我声明了一个新变量,尽管我的方法没有使用strcpy,但您可以谨慎地重复使用原始变量。无论如何,如果您的输入总是(xxx)xxx xxxx xxxx形式,并且您的输出总是xxx xxx xxxx,那么为什么不做:

    char newPhone[14];
    newPhone[0] = phoneNum[1];
    newPhone[1] = phoneNum[2];
    newPhone[2] = phoneNum[3];
    newPhone[3] = '-';
    newPhone[4] = phoneNum[6];
    newPhone[5] = phoneNum[7];
    newPhone[6] = phoneNum[8];
    newPhone[7] = '-';
    newPhone[8] = phoneNum[10];
    newPhone[9] = phoneNum[11];
    newPhone[10] = phoneNum[12];
    newPhone[11] = phoneNum[13];
    newPhone[12] = '\0';
    

    蛮力?当然可以,但是如果您的输入和输出总是像您所说的那样,那么它应该高效地运行。

        3
  •  1
  •   indiv Olivier Poulin    17 年前

    我想我只是太慢了。在memmove()上执行此操作并不聪明,但它展示了如何创建一个循环,并且仍然可以从内部进行所有比较:

    char *formatPhoneNum(char *buffer) {
            int index = 0;
            for( index = 0; index < 12; ++index ) {
                    buffer[index] = buffer[index + 1];
            }
            buffer[3] = '-';
            buffer[12] = '\0';
    
            return buffer;
    }
    

    如果返回所修改字符串的开头,而不是仅返回void,这样可以更轻松地链接命令,您可能会发现这很有帮助。例如。,

    printf("%s\n", formatPhoneNum(buffer));
    
        4
  •  0
  •   Pavel Minaev    17 年前

    首先,这是错误的:

    strcpy( phoneNum, phoneNum + 1 );
    

    strcpy :

    如果复制发生在重叠的对象之间,则行为未定义。

    这里的“对象”是源和目标 char 数组。顺便说一句,MSDN同意这一点,所以至少在一个流行的现实世界实现中,这不会正常工作。

    似乎更简单的方法是让函数返回指针的一个新的“适当”值(到同一个缓冲区中),这样它就可以将其调整1来调整指针 '('

    您的验证只计算数字,允许格式化,例如 "1-234567890" "1234567890-" 甚至 "12345foobar4567890" -这可能是问题,也可能不是问题,具体取决于需求。

        5
  •  0
  •   pmg    17 年前

    static void formatPhoneNum(char *dst, const char *src);
    

    甚至,返回新编号的长度:

    static int formatPhoneNum(char *dst, const char *src);
    

    src dst 在正确的位置插入破折号。调用方负责在中提供空间 dst 并检查返回值:如果是12(包括破折号),则全部ok;否则就会出错。

    您可以返回一些负数以指示可能的错误。例如:-1表示 src 时间不够长-2表示的格式不正确

    请记录所有返回值!

    哦别忘了终止 dst !

        6
  •  0
  •   Ashwin    17 年前

    如果允许您更改API,您可以接受字符**或返回字符*,并提高时间复杂度:

    static void formatPhoneNum(char **phoneNum) {
      (*phoneNum)[4] = '-';
      (*phoneNum)[13] = '\0';
      (*phoneNum)++;
    }
    

    交替地

    static char *formatPhoneNum(char *phoneNum) {
      phoneNum[4] = '-';
      phoneNum[13] = '\0';
      return phoneNum + 1;
    }
    

    优点是这将需要恒定的时间。