Нужен лучший способ отформатировать номер телефона в C - PullRequest
2 голосов
/ 18 сентября 2009

У меня есть массив символов, который содержит номер телефона в форме: «(ххх) ххх-хххх хххх», и мне нужно преобразовать его во что-то в форме: «ххх-ххх-хххх», где я просто обрежу расширение. Мой начальный проход в функции выглядит так:

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 ]

6 голосов
/ 18 сентября 2009

Поскольку вы указали, что ваш ввод гарантированно будет в правильном формате, как насчет следующего:

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

memmove () гарантированно работает с перекрывающимися буферами

2 голосов
/ 18 сентября 2009

Как сказал Павел, вы не можете натянуть струну на себя. Я объявляю новую переменную для ясности, хотя мой подход не использует 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';

Грубая сила? Конечно, но - если ваши входы и выходы всегда будут такими, как вы заявляете - они должны работать эффективно.

1 голос
/ 18 сентября 2009

Ну, я думаю, я слишком медленный. Ничего умного в этом нет по сравнению с 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));
0 голосов
/ 18 сентября 2009

Если вам разрешено изменять 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;
}

Преимущество состоит в том, что это займет постоянное время.

0 голосов
/ 18 сентября 2009

Когда это возможно (а не снижение производительности), я предпочитаю передавать данные в функции как константы. В вашем случае я не вижу причин не делать этого, поэтому я бы объявил вашу функцию как

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

или даже, возвращая длину нового номера:

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

Затем просто скопируйте цифры из src в dst, вставив тире в нужные места. Вызывающий отвечает за предоставление места в dst и проверку возвращаемого значения: если 12 (включая тире), все в порядке; в противном случае произошла ошибка.

Вы можете вернуть отрицательное число, чтобы указать возможные ошибки. Например: -1 будет означать, что src недостаточно длинный; -2 означает неправильный формат для src и т. Д. *

Документировать все возвращаемые значения!

Oh! И не забывайте, чтобы NUL прекращал dst!

0 голосов
/ 18 сентября 2009

Для начала, это неправильно:

strcpy( phoneNum, phoneNum + 1 );

, поскольку в стандарте ISO C говорится о strcpy:

Если копирование происходит между объектами, которые перекрываются, то поведениеundefined.

"объектами" здесь являются массивы источника и назначения char.Кстати, MSDN согласен с этим, поэтому он не будет работать должным образом хотя бы в одной популярной реальной реализации.

Кажется, что более простой подход - заставить функцию возвращать новое "правильное"значение указателя (в тот же буфер), поэтому он может изменить его на 1, чтобы обрезать '('.

Ваша проверка, которая просто считает цифры, разрешает форматирование, такое как "1-234567890" или "1234567890-"или даже "12345foobar4567890" - это может или не может быть проблемой, в зависимости от требований.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...