使用regex,java从电话号码中删除可选的零

时间:2018-02-05 17:08:55

标签: java android regex phone-number

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

我自己编写了最后一种方法(删除可选的零),但我希望它更直接一些(可能使用正则表达式来识别可选的零AND并将其删除同样的正则表达式,就像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部分还可以包括不应该删除的零,其他可选字符如 - 和+也不应该受到影响等),但为了简单起见我把他们留了出去。如果您考虑电话号码,您可以自己猜测其余部分。

3 个答案:

答案 0 :(得分:1)

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

(\(0\)) 

我确信有可用的Java API可以访问各个匹配组,并用您喜欢的任何内容替换它们。

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

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

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

或者只是按原样保留您的代码。它是可自动记录的可读代码。正则表达式需要文档。

答案 1 :(得分:1)

经过一夜的休息,我提出了一个更简单的问题,我在问题中提供的例子:拆分正则表达式可用于替换,它甚至更好,因为它也会替换(012)区域代码的可选0(最后一个钩子将替换为所有其他可选字符)。

最重要的是,我的示例代码也删除了电话号码中的所有其他零(我认为这是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这样的情况存在,并且钩子仅用于区域代码的(部分)。如果没有,我很乐意听到它!

答案 2 :(得分:0)

可以试试这个

extension ThirdViewController: UICollectionViewDelegate, UICollectionViewDataSource {


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        return categoryList.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! UserCategoryCollectionViewCell

        cell.categoryLbl.text = categoryList[indexPath.row]

        if Defaults.hasKey(.categoryListIndices) {
            if (Defaults[.categoryListIndices]?.contains(indexPath.row))! {
                cell.alpha = 0.1
                cell.isSelected = true
            } else {

                cell.alpha = 1

            }
        }

        return cell
    }


    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! UserCategoryCollectionViewCell


        cell.alpha = 0.1

    }

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        let deselectedCell = collectionView.cellForItem(at: indexPath) as? UserCategoryCollectionViewCell
        deselectedCell?.alpha = 1

        print(deselectedCell?.isSelected)
        collectionView.deselectItem(at: indexPath, animated: false)
    }

}