Скрываем часть номера телефона

    Представьте, что вам нужно скрыть часть номер под звездочками. Заменить +79999999999 на +799****9999 не трудно, а теперь представьте, что масок номеров не одна, а на много больше, номера эти как российские, так и канадские или любые другие. В этой функции я постарался захватить как можно больше номеров.

    Является строка номером телефона:

    /^(\(?\+?\d{1,2}\)? ?\(?\d{1,3}\)? ?\d+\-? ?\d+\-? ?\d+)$/

    Кушает:

    '+7 999 999-99-99',
    '+79999999999',
    '79999999999',
    '+7 (495) 150 41-32',
    '+55 11 99999-5555',
    '(+44) 0848 9123 456',
    '+1 284 852 5500',
    '+1 345 9490088',
    '+32 2 702-9200',
    '+65 6511 9266',
    '+86 21 2230 1000',
    '(123) 456 7899',
    '123-456-7899',
    '123 456 7899',
    '1234567899',
    '1 800 5551212',
    '800 555 1212',
    '8005551212',
    '18005551212',
    '234-911-5678',
    '+54 9 2982 123456',
    '02982 15 123456',
    '(719) 309-3829'
    

    Функция замены:

    function hideNumber(string, replaceTo = '*', elemsHide = 4, sliceFromback = 4) {
        result = string.match(/^(\(?\+?\d{1,2}\)? ?\(?\d{1,3}\)? ?\d+\-? ?\d+\-? ?\d+)$/);
        if (result !== null) {
              // тут мы выдергиваем n элементов после среза x
            const regex = new RegExp(`((\\(?\\ ?\\-?\\d\\ ?\\-?\\)?){${elemsHide}})((\\ ?\\-?\\d\\ ?\\-?){${sliceFromback}}$)`, 'gm');
    
            let m;
            while ((m = regex.exec(string)) !== null) {
                if (m.index === regex.lastIndex) {
                    regex.lastIndex++;
                }
    
                const forRex = m[1];
                const str = m[1].replace(/(\d)/gm, replaceTo);
                const lasts = m[3];
                const full = string;
                const noBack = full.slice(0, -lasts.length).slice(0, -forRex.length);
                const out = noBack+''+str+''+lasts;
                return out;
            }
            return string;
        } else {
            return string;
        }
    }
    

    Демо
    const str = [
        '+7 999 999-99-99',
        '+79999999999',
        '79999999999',
        '+7 (495) 150 41-32',
        '+55 11 99999-5555',
        '(+44) 0848 9123 456',
        '+1 284 852 5500',
        '+1 345 9490088',
        '+32 2 702-9200',
        '+65 6511 9266',
        '+86 21 2230 1000',
        '(123) 456 7899',
        '123-456-7899',
        '123 456 7899',
        '1234567899',
        '1 800 5551212',
        '800 555 1212',
        '8005551212',
        '18005551212',
        '234-911-5678',
        '+54 9 2982 123456',
        '02982 15 123456',
        '(719) 309-3829',
        'Judi'
    ];
    
    
    str.forEach(i => {
        console.log(i +' => '+hideNumber(i));
    })
    

    console:

    +7 999 999-99-99 => +7 99* ***-99-99
    +79999999999 => +799****9999
    79999999999 => 799****9999
    +7 (495) 150 41-32 => +7 (49*) *** 41-32
    +55 11 99999-5555 => +55 11 9****-5555
    (+44) 0848 9123 456 => (+44) 084* ***3 456
    +1 284 852 5500 => +1 28* *** 5500
    +1 345 9490088 => +1 34* ***0088
    +32 2 702-9200 => +32 * ***-9200
    +65 6511 9266 => +65 **** 9266
    +86 21 2230 1000 => +86 21 **** 1000
    (123) 456 7899 => (12*) *** 7899
    123-456-7899 => 12*-***-7899
    123 456 7899 => 12* *** 7899
    1234567899 => 12****7899
    1 800 5551212 => 1 80* ***1212
    800 555 1212 => 80* *** 1212
    8005551212 => 80****1212
    18005551212 => 180****1212
    234-911-5678 => 23*-***-5678
    +54 9 2982 123456 => +54 9 29** **3456
    02982 15 123456 => 02982 ** **3456
    (719) 309-3829 => (71*) ***-3829
    Judi => Judi 
    


    Если один из ваших номеров (который мог бы существовать на деле) не подошел, пишите, дополню.

    P.S.


    В комментарии mwizard добавил реализацию без регулярки:

    type Context = {
        readonly offset: number;
        readonly filtered: string;
    }
    
    function hidePhone(phone: string): string {
        return Array.from(phone).reduceRight((ctx: Context, char: string): Context => {
            const isDigit = char >= '0' && char <= '9';
            const offset = ctx.offset + (isDigit ? 1 : 0);
            const filteredChar = isDigit ? (offset >= 4 && offset < 8) ? '*' : char : char;
            const filtered = filteredChar + ctx.filtered;
            return { offset, filtered };
        }, { offset: -1, filtered: '' }).filtered;
    }
    
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 20

      –13
      Если один из ваших номеров (который мог бу существовать на деле) не подошел, пишите, дополню.

      Спасибо, но лучше я сам у себя проверю и если понадобится — исправлю.
        +17

        “У вас есть проблема. Вы решили использовать регулярные выражения чтобы её решить. Теперь у вас две проблемы.”

          +3

          Обычно телефонные номера проходят валидацию, так что все равно к какому-то определенному формату приводить надо. Ну и хранить в БД "как юзер ввёл" тоже, на мой взгляд, не очень кошерно.


          Но как пример сферического коня в вакууме, вполне нарм.

            +5

            Оооо! «Мама, я постиг простенькие регулярки.» Не нужно так, этот код потом людям придется сопровождать.


            1. Откусили голову (если есть) по шаблону /(?<=+|\A)\d+(?=\D)/
            2. Выбросили все, что не цифра — из того, что осталось.
            3. Добавили код страны (если п. 1 вернул значение), или дефолт.

            Две строчки кода, внятные и короткие.

              0

              По-вашему это номер телефона: (+12 (123 456 789-000

                0
                это не номер телефона — но не из-за скобок а потому что там на три цифры больше чем нужно для странового кода +1

                а наличие\отсутствие скобок… модем, мобильный телефон, астериск — они просто проигнорируют эти символы и наберут строку "(+12 (123 456 789-" как нормальный североамериканский номер.
                  +3
                  Код страны может быть больше чем 2 цифры: +380 57 700 0046 (Укртелеком, г. Харьков, Украина)
                  Код города может быть больше чем 3 цифры: +7 (86196) 7-19-86 (Россельхозбанк, г. Тихорецк, Краснодарский край, Россия)
                    +2
                    type Context = {
                        readonly offset: number;
                        readonly filtered: string;
                    }
                    
                    function hidePhone(phone: string, replaceTo = '*', elemsHide = 4, sliceFromback = 4): string {
                        const rangeStart = sliceFromback;
                        const rangeEnd = sliceFromback + elemsHide;
                    
                        return Array.from(phone).reduceRight((ctx: Context, char: string): Context => {
                            const isDigit = char >= '0' && char <= '9';
                            const offset = ctx.offset + (isDigit ? 1 : 0);
                            const filteredChar = isDigit && (offset >= rangeStart && offset < rangeEnd) ? replaceTo : char;
                            const filtered = filteredChar + ctx.filtered;
                            return { offset, filtered };
                        }, { offset: -1, filtered: '' }).filtered;
                    }
                    
                    console.log(hidePhone('+12(345) 678-90-12 ;D'));
                      0
                      да, этот вариант получше, не подумал, спасибо.
                      0
                      А ещё есть локальные номера. В том же Петербурге — не обязательно указывать префикс 812 для локальных вызовов. Т.е. здесь больше вопрос являются ли номера нормализованными или нет. Кстати, это вызывает проблемы, т.к., скажем, по базе 812*******, +7812******* и 8812******* тупым сравнением являются разными номерами. А ещё есть прикол с мобильными с прямыми городскими. Тема обширная
                        +4
                        Удивительно причудливое решение. Возможно, так будет проще?

                        phoneNumber.replace(/\d/g, (current) => {
                          this.counter = this.counter || 0; 
                          this.counter++;  
                          return (this.counter > 3 && this.counter < 7) ? '*' : current
                          }
                        )
                        


                        Вместо 3 и 7 можете вставлять какие-то свои min и max.
                          +1

                          Так проще, но по условиям нужно идти с обратной стороны.

                            0
                            JS-way!

                            phoneNumber.split('').reverse().join('').replace(/\d/g, (current) => {
                              this.counter = this.counter || 0; 
                              this.counter++;  
                              return (this.counter > 3 && this.counter < 7) ? '*' : current
                              }
                            ).split('').reverse().join('');
                            


                            Хотя смысл это имеет разве что эзотерический. Что значит ваш комментарий про обратную сторону?
                              0

                              Я только сейчас обратил внимание на вашу дописку к комментарию. "Идти с обратной стороны" значит, что если у вас последовательность варьруемой длины (то 8 знаков, то 15), вам всегда нужно оставить последние 4 цифры целыми, и закрасить еще 4 цифры с конца.


                              Впрочем, с эзотерическим смыслом соглашусь, т.к. если уж хранить ненормализованные данные, так нефильтрованные, а раз фильтровать, так все равно нужно предварительно нормализовать.

                          0

                          Вики подсказывает, что форматов телефонов очень много и там все сложно, а значит регулярки выкидываем на свалку и делаем простой LL парсер. А ещё лучше — libphonenumber.

                            +2

                            Лет 10 назад аутсорсили для одного российского сотового оператора плагины для IE, FF, Outlook. Задача была находить на странице телефонные номера, подсвечивать их, добавлять флажок и пару кнопок.
                            Так вот, сначала пробовали регулярками. Но тестировщики постоянно находили примеры где регулярки не работают. Потом я плюнул и написал обычную функцию на js, как когда-то в универе на си делал. Заработало, причём на порядок быстрее регулярок (на тестовой странице было 100+ номеров с разным контентом).
                            Ещё достаточно нетривиальной задачей было определение страны по номеру телефона. Во-первых разные форматы номеров, как локальных так и международных. Во-вторых, коды стран от 1 до 4 цифр (см. Википедию). США с Канадой где номера могут на +1 начинаться и т.п.

                              +1
                              А есть еще буквенные номера вроде 1-900-MICROSOFT-22 (на самом деле это цифровой номер, а буквы просто матчатся в цифры, но людям так проще запоминать)
                              +1
                              А потом хакер пытаясь взломать какого-то человека — узнаёт разные наборы цифр на разных сервисах и получает цельный номер. Были прецеденты.

                              Хороший вариант — оставлять последние две цифры, как делают некоторые крупные сайты (конкретно инстаграм сейчас вспомнился).

                              Кроме того из номера следует убрать всё кроме цифр.

                              В такой постановке — это будет существенно более простая задача.
                                +2
                                А потом оказывается, что у пользователя два номера, и у обоих последние две цифры совпадают. Были прецеденты.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое