荷兰电话号码的正则表达式

3

在正则表达式方面,我并不是最差的,但这个问题让我束手无策。

简而言之,这是我目前拥有的代码。

$aNumbers = array(
    '612345678',
    '546123465',
    '131234567',
    '+31(0)612345678'
);

foreach($aNumbers as $sNumber) {
    $aMatches = array();
    $sNumber = preg_replace('/(\(0\)|[^\d]+)/', '', $sNumber);

    preg_match('/(\d{1,2})?(\d{3})(\d{3})(\d{3})$/', $sNumber, $aMatches);

    var_dump($sNumber);
    var_dump($aMatches);
}

简单来说,我想匹配特定的电话号码格式,以确保统一的显示。

+31(0)612345678
+31(0)131234567

两个去掉 + 和 (0) 的内容将不再包含这些字符。 分为几部分:

31     6    123 456 78
Country Net Number
31     13   123 456 78
Country Net Number

现在,在某些情况下,+31(或+1、+222)是可选的。6和13始终包括其中,但作为有趣的变化,以下格式也是可能的:

31     546  123 456
Country Net Number

这在正则表达式中是否可能实现?

https://dev59.com/Q3XYa4cB1Zd3GeqP2BQs#17949938 - leeor
是的,但如果我们不知道所有的网络号码,就不能这样做。 - Kanti
1
你确定第二个例子结尾是8吗?这似乎与你之前缩短部分的数字不符。 - Mathiou
数据从哪里来?理想情况下,您不应该这样做。当用户填写表格时,调用代码和电话号码应该在它们自己的单独字段中,并且您只需要对其进行验证清理即可。 - Sverri M. Olsen
2个回答

1
我已回答过几个这种类型的问题,我的策略是识别传达含义的格式或数字关系的某些部分,并摆脱其余部分。 其中一个解析非NANP号码格式的示例使用解析表达式中的有效地区代码列表,并在存在时确定国家代码。 它提取国家代码、地区代码,然后是其余的号码。
对于您的国家,我假设HansM答案中的区域/网络/地区代码列表是正确的或易于替换的,因此我猜想这个正则表达式的修改可能会有用:
^[ -]*(\+31)?[ -]*[(0)]*[ -]*(7|43|32|45|33|49|39|31|47|34|46|41|90|44|351|353|358)[ -]*((?:\d[ -]*)+)

它首先匹配国家代码(如果存在),并将其存储在反向引用1中,然后忽略一个零。然后它将匹配区域/网络/区域代码之一,并将其存储在反向引用2中。然后获取任意数量的数字(一个或多个),与破折号(-)和/或空格()混合,并将这些存储到反向引用3中。
此后,您可以解析第三个编号组以进行有效性检查或进一步重新格式化。
我正在Regex 101上测试它,但我可以使用可接受和不可接受输入列表,以及当可接受时应如何重新格式化...
[编辑]
我已经使用this list of city codes for the Netherlands并相应修改了表达式:
^[ -]*(\+31)?[ -]*[(0)]*[ -]*([123457]0|23|24|26|35|45|71|73|570)[ -]*((?:\d[ -]*)+)

它执行以下解析操作:
input                     (1)    (2)    (3) 
---------------------    ------ ------ ---------------
0707123456                       70     7123456
0267-123456                      26     7-123456
0407-12 34 56                    40     7-12 34 56
0570123456                       570    123456
07312345                         73     12345
+31(0)734423211           +31    73     4423211 

但我仍然不知道那对你是否有帮助。
【编辑2】
维基百科有一个更全面的代码列表
"010、0111、0113、0114、0115、0117、0118、013、015、0161、0162、0164、0165、0166、0167、0168、0172、0174、0180、0181、0182、0183、0184、0186、0187、020、0222、0223、0224、0226、0227、0228、0229、023、024、0251、0252、0255、026、0294、0297、0299、030、0313、0314、0315、0316、0317、0318、0320、0321、033、0341、0342、0343、0344、0345、0346、0347、0348、035、036、038、040、0411、0412、0413、0416、0418、043、045、046、0475、0478、0481、0485、0486、0487、0488、0492、0493、0495、0497、0499、050、0511、0512、0513、0514、0515、0516、0517、0518、0519、0521、0522、0523、0524、0525、0527、0528、0529、053、0541、0543、0544、0545、0546、0547、0548、055、0561、0562、0566、0570、0571、0572、0573、0575、0577、0578、058、0591、0592、0593、0594、0595、0596、0597、0598、0599、070、071、072、073、074、075、076、077、078、079"
可以在代码选择部分使用它,如下所示(如果您希望更容易阅读和更新):
10|111|113|114|115|117|118|13|15|161|162|164|165|166|167|168|172|174|180|181|182|183|184|186|187|20|222|223|224|226|227|228|229|23|24|251|252|255|26|294|297|299|30|313|314|315|316|317|318|320|321|33|341|342|343|344|345|346|347|348|35|36|38|40|411|412|413|416|418|43|45|46|475|478|481|485|486|487|488|492|493|495|497|499|50|511|512|513|514|515|516|517|518|519|521|522|523|524|525|527|528|529|53|541|543|544|545|546|547|548|55|561|562|566|570|571|572|573|575|577|578|58|591|592|593|594|595|596|597|598|599|70|71|72|73|74|75|76|77|78|79

或者像这样(如果您更喜欢更高效的表达式计算):

1([035]|1[134578]|6[124-8]|7[24]|8[0-467])|2([0346]|2[2346-9]|5[125]|9[479])|3([03568]|1[34-8]|2[01]|4[1-8])|4([0356]|1[12368]|7[58]|8[15-8]|9[23579])|5([0358]|[19][1-9]|2[1-5789]|4[13-8]|6[126]|7[0-3578])|7[0-9]

明天回到工作时,我会尝试将这个解决方案融入进去,但你的解决方案看起来不错。如果它能够正常运行,我会给它评分并标记为已解决,谢谢! - ReSpawN
@ReSpawN - 听起来不错 - 如果你需要帮助重新格式化剩下的数字,我也可以尝试帮忙。 - Code Jockey

0
我已经使用了Nuget软件包libphonenumber-csharp。
这帮助我创建了一个(荷兰)电话号码验证器,以下是代码片段。没有我的解决方案的其他部分,它将无法编译,但至少你可以了解如何处理这个问题。
  public override void Validate()
    {
        ValidationMessages = new Dictionary<string, string>();
        ErrorMessage = string.Empty;
        string phoneNumber;
        string countryCode = _defaultCountryCode;

        // If the phoneNumber is not required, it is allowed to be empty.
        // So in that case isValid gets defaultvalue true
        bool isValid = (!_isRequired);

        if (!string.IsNullOrEmpty(_phoneNumber))
        {
            var phoneUtil = PhoneNumberUtil.GetInstance();
            try
            {
                phoneNumber = PhoneNumbers.PhoneNumberUtil.Normalize(_phoneNumber);
                countryCode = PhoneNumberUtil2.GetRegionCode(phoneNumber, _defaultCountryCode);
                PhoneNumber oPhoneNumber = phoneUtil.Parse(phoneNumber, countryCode);
                var t1 = oPhoneNumber.NationalNumber;
                var t2 = oPhoneNumber.CountryCode;
                var formattedNo = phoneUtil.Format(oPhoneNumber, PhoneNumberFormat.E164);
                isValid = PhoneNumbers.PhoneNumberUtil.IsViablePhoneNumber(formattedNo);
            }
            catch (NumberParseException e)
            {
                var err = e.ToString();
                isValid = false;
            }
        }

        if ((isValid) && (!string.IsNullOrEmpty(_phoneNumber)))
        {
            Regex regexValidator = null;
            string regex;

            // Additional validations for Dutch phone numbers as LibPhoneNumber is to graceful as it comes to
            // thinking if a number is valid.
            switch (countryCode)
            {
                case "NL":

                    if (_phoneNumber.StartsWith("0800") || _phoneNumber.StartsWith("0900"))
                    {
                        // 0800/0900 numbers
                        regex = @"((0800|0900)(-| )?[0-9]{4}([0-9]{3})?$)";
                        regexValidator = new Regex(regex);
                        isValid = regexValidator.IsMatch(_phoneNumber);
                    }
                    else
                    {
                        string phoneNumberCheck = _phoneNumber.Replace("(", "").Replace(")", "").Replace("-", "").Replace(" ", "");

                        regex = @"^(0031|\+31|0)[1-9][0-9]{8}$";

                        regexValidator = new Regex(regex);
                        isValid = regexValidator.IsMatch(phoneNumberCheck);
                    }
                    break;
            }
        }
        if (!isValid)
        {
            ErrorMessage = string.Format(TextProvider.Get(TextProviderConstants.ValMsg_IsInAnIncorrectFormat_0),
                ColumnInfoProvider.GetLabel(_labelKey));

            ValidationMessages.Add(_messageKey, ErrorMessage);
        }
    }

我的 PhoneNumberUtil2 类可能也很有用,它是在 nuget 包 libphonenumber-csharp 的基础上构建的:

// Code start
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using PhoneNumbers;

namespace ProjectName.Logic.Miscellaneous
{
    public class PhoneNumberUtil2
    {
        /// <summary>
        /// Returns the alphanumeric country code for a normalized phonenumber. If a phonenumber does not contain 
        /// an international numeric country code, the default country code for the website is returned.
        /// This works for 17 countries: NL, GB, FR, DE, BE, AU, SE, NO, IT, TK, RU, CH, DK, IR, PT, ES, FI
        /// </summary>
        /// <param name="normalizedPhoneNumber"></param>
        /// <param name="defaultCountryCode"> </param>
        /// <returns></returns>
        public static string GetRegionCode(string normalizedPhoneNumber, string defaultCountryCode)
        {
            if (normalizedPhoneNumber.Length > 10)
            {
                var dict = new Dictionary<string, string>();
                dict.Add("7", "RU");
                dict.Add("43", "AT");
                dict.Add("32", "BE");
                dict.Add("45", "DK");
                dict.Add("33", "FR");
                dict.Add("49", "DE");
                dict.Add("39", "IT");
                dict.Add("31", "NL");
                dict.Add("47", "NO");
                dict.Add("34", "ES");
                dict.Add("46", "SE");
                dict.Add("41", "CH");
                dict.Add("90", "TR");
                dict.Add("44", "GB");
                dict.Add("351", "PT");
                dict.Add("353", "IE");
                dict.Add("358", "FI");

                // First check 3-digits International Calling Codes
                if (dict.ContainsKey(normalizedPhoneNumber.Substring(0, 3)))
                {
                    return dict[normalizedPhoneNumber.Substring(0, 3)];
                }

                // Then 2-digits International Calling Codes 
                if (dict.ContainsKey(normalizedPhoneNumber.Substring(0, 2)))
                {
                    return dict[normalizedPhoneNumber.Substring(0, 2)];
                }

                // And finally 1-digit International Calling Codes
                if (dict.ContainsKey(normalizedPhoneNumber.Substring(0, 1)))
                {
                    return dict[normalizedPhoneNumber.Substring(0, 1)];
                }
            }
            return defaultCountryCode;
        }

    }
}

1
虽然 OP 不需要 .NET 解决方案,但深入研究库可能会为这个问题提供正确的正则表达式... - tofutim
你可能是对的,抱歉。但实际上,使用不同的正则表达式的 switch 语句仍然可能很有用。 - HansM
虽然我不能流利地打出.NET,但我仍然可以像其他程序员一样阅读它 - 逻辑是正确的。不幸的是,这并不是我需要的,但还是谢谢! - ReSpawN
抱歉,我无法帮助您,但归根结底,我无法使用一个正则表达式解决它,而是需要在 switch 语句中使用两个正则表达式,并在检查数字之前进行一些预验证和格式化。也许您可以在您的代码中遵循类似的逻辑。 - HansM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接