【问题标题】:Remove optional zero from phone number using regex, java使用正则表达式,java从电话号码中删除可选零
【发布时间】:2018-02-05 17:08:54
【问题描述】:

我有一个 java 类(称为 PhoneNumber)来验证和处理电话号码。

我自己编写了最后一个方法(删除可选的零),但我希望它更简单一些(可能使用正则表达式来识别可选的零并使用相同的正则表达式删除它,就像OPTIONAL_PHONE_NUMBER_CHARACTERS 部分)。这段代码感觉很脆弱而且不是 100% 正确,尽管我认为它可以满足我的需要并且涵盖了我能想到的所有单元测试。

谁能给我一个包含正确正则表达式的清晰示例? (也欢迎其他简单的解决方案)。

private static final Pattern VALID_PHONE_NUMBER_REGEX = Pattern.compile("\\+?[0-9#*]{1,20}");
private static final Pattern OPTIONAL_PHONE_NUMBER_CHARACTERS = Pattern.compile("[\\s()/.-]");

public static boolean isValid(String phoneNumber)
{
    if (phoneNumber == null || phoneNumber.isEmpty())
    {
        return false;
    }

    String compactPhoneNumber = removeOptionalCharacters(phoneNumber);
    return VALID_PHONE_NUMBER_REGEX.matcher(compactPhoneNumber).matches();
}

public static String removeOptionalCharacters(String phoneNumber)
{
    String phoneNumberWithoutOptionalZero = removeOptionalZero(phoneNumber);
    return OPTIONAL_PHONE_NUMBER_CHARACTERS.matcher(phoneNumberWithoutOptionalZero).replaceAll("");
}

private static final String OPTIONAL_ZERO = "(0)";
private static final String OPTIONAL_ZERO_SPLIT_REGEX = Pattern.quote("(0");

public static String removeOptionalZero(String phoneNumber)
{
    String[] split = phoneNumber.split(OPTIONAL_ZERO_REGEX);
    if (split.length == 2 && !split[0].isEmpty())
    {
        return phoneNumber.replaceAll(OPTIONAL_ZERO, "");
    }
    return phoneNumber;
}

如您所见,我提取了用于删除可选字符的代码,以防我需要使用电话号码拨打电话。例如。 +31 12-3456-789 会变成+31123456789

我想使用 removeOptionalCharacters 方法进行的转换是:

+31 (0)12 3456 789   >   +31123456789
+31 (012) 3456 789   >   +31123456789
(0)12 3456 789       >   0123456789
(012) 3456 789       >   0123456789

总结一下,这些是应该成功的单元测试:

@Test
public void removeOptionalCharacters_HooksAroundOptionalZero_ZeroIsRemoved()
{
    String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("+31 (0)12 3456789");
    assertEquals("+31123456789", compactPhoneNumber);
}

@Test
public void removeOptionalCharacters_HooksAroundAreaCode_ZeroIsRemoved()
{
    String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("+31 (012) 3456789");
    assertEquals("+31123456789", compactPhoneNumber);
}

@Test
public void removeOptionalCharacters_HooksAroundOptionalZeroWithoutCountryCode_ZeroIsNotRemoved()
{
    String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("(0)12 3456789");
    assertEquals("0123456789", compactPhoneNumber);
}

@Test
public void removeOptionalCharacters_HooksAroundAreaCodeWithoutCountryCode_ZeroIsNotRemoved()
{
    String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("(012)3456789");
    assertEquals("0123456789", compactPhoneNumber);
}

ps。我认为这些测试涵盖了所有带有可选零的常见情况。当然还有更多的单元测试来涵盖整个事情(3456789 部分还可以包括不应删除的零,并且其他可选字符(如 - 和 + 也不应该受到影响等),但为了简单起见,我已经离开了他们出去。如果您考虑电话号码,您将能够自己猜到其余的。

【问题讨论】:

  • +31 012 3456 789+30 (0)12 3456 789 号码呢?
  • 把这个提交给code review可能会更好。 “感觉脆弱”本身并不是真正的技术问题。你所拥有的基本上是有效的,但它可以做得更好。
  • 阿努巴瓦,你是什么意思? +31 012 3456 789 没有区号,所以是有效的电话号码还是什么?也许您可以在解释中包含该示例的国家/地区?如果 +30 (0)12 3456 789 有 012 作为区号(所以“挂钩”0 是可选的),我希望它变成 +30123456789
  • MarsAtomic,真的,谢谢,我不知道网站的代码审查部分!因为“最佳答案”也值得商榷,所以我不会在这里指定一个,只有在我认为它对我有帮助的情况下才会投票。

标签: java android regex phone-number


【解决方案1】:

我会使用两个正则表达式。一种用于验证电话号码,另一种用于对其进行规范化,省略可选字符。 您的规范化正则表达式应包含独占匹配组(由 | 分隔)。 (0) 的匹配组将是

(\(0\)) 

我确信有 Java API 可用于访问各个匹配组并将其替换为您喜欢的任何内容。

结合 (0) 和其他字符的正则表达式将是:

(\(0\))|([\\s()/.-])

仅用“”替换第一个匹配组的第一个匹配项。 将第二组的所有匹配替换为“”

或者直接保留您的代码。它是自记录的可读代码。正则表达式需要文档。

【讨论】:

    【解决方案2】:

    经过一夜的休息,我想出了我在问题中提供的示例的一个更简单的版本:拆分正则表达式可用于替换,而且更好,因为它将替换可选的 0 (012) 区号(无论如何,最后一个钩子将被所有其他可选字符替换)。

    除此之外,我的示例代码还删除了电话号码中所有额外的零(我认为这是 Anubhava 在评论中的意思的一部分)。我的新解决方案也解决了这个问题:

    private static final String OPTIONAL_ZERO_REGEX = Pattern.quote("(0");
    
    public static String removeOptionalZero(String phoneNumber)
    {
        String[] split = phoneNumber.split(OPTIONAL_ZERO_REGEX);
        if (split.length == 2 && !split[0].isEmpty())
        { // Only remove the optional zero when preceded by a country code
            return phoneNumber.replaceFirst(OPTIONAL_ZERO_REGEX, "");
        }
        return phoneNumber;
    }
    

    replaceFirst 而不是 replaceAll 更简洁一些,尽管我认为它在实践中不会有所作为。

    额外的单元测试证明它适用于非可选零:

    @Test
    public void removeOptionalCharacters_HooksAroundAreaCodeAndManyAdditionalZeroes_ZeroIsRemoved()
    {
        String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("+30 (005) 0511010");
        assertEquals("+30050511010", compactPhoneNumber);
    }
    
    @Test
    public void removeOptionalCharacters_HooksAroundAreaCodeWithoutCountryCodeAndManyAdditionalZeroes_ZeroIsNotRemoved()
    {
        String compactPhoneNumber = PhoneNumber.removeOptionalCharacters("(005)0511010");
        assertEquals("0050511010", compactPhoneNumber);
    }
    

    编辑: 另一种选择:

    private static final String OPTIONAL_ZERO = "(0";
    private static final String OPTIONAL_ZERO_REGEX = Pattern.quote(OPTIONAL_ZERO);
    
    public static String removeOptionalZero(String phoneNumber)
    {
        if (phoneNumber.indexOf(OPTIONAL_ZERO) > 0)
        { // Only remove the optional zero when preceded by a country code
            return phoneNumber.replaceAll(OPTIONAL_ZERO_REGEX, "");
        }
        return phoneNumber;
    }
    

    ps。如果可能,最后一个示例将多次删除(0。但是,我认为不存在像 (0031)(012)3456789 这样的情况,并且这些挂钩仅用于(部分)区号周围。如果没有,我很想听听!

    【讨论】:

      【解决方案3】:

      可以试试这个

      ([+]*)([ ])*(\w)?([\d]+)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-02
        • 1970-01-01
        • 2019-05-23
        • 1970-01-01
        • 2017-09-23
        • 2014-02-15
        • 2017-03-02
        相关资源
        最近更新 更多