在不使用 Regex 的情况下,下划线、点和破折号后面必须始终跟一个或多个字母数字字符

An underscore, a dot and a dash must always be followed by one or more alphanumeric characters without using Regex

我正在做一项不使用 Regex 验证电子邮件的学校作业。本练习的前提是学习方法并练习我们的批判性思维。我知道代码可以减少到更少的行数。

现在,我需要检查电子邮件前缀的所有条件('@'之前的字符):

有效前缀的示例是:“abc-d”、“abc.def”、“abc”、“abc_def”。
无效前缀的示例是:“abc-”、“abc..d”、“.abc”、“abc#def”。

我很难弄清楚第三个条件。到目前为止,我有这些满足其他条件的方法。

public static boolean isAlphanumeric(char c) {
    return Character.isLetterOrDigit(c);
}

public static boolean isValidPrefixChar(char preChar) {
    char[] prefixChar = new char[] {'-', '.', '_'};
    
    for (int i = 0; i < prefixChar.length; i++) {
        if (prefixChar[i] == preChar) {
            return true;
        } else if (isAlphanumeric(preChar)) {
            return true;
        }
    }
    return false;
}

public static boolean isValidPrefix(String emailPrefix) {
    boolean result = false;
    
    // To check if first character is alphanumeric
    if (isAlphanumeric(emailPrefix.charAt(0)) && emailPrefix.length() > 1) {
        for (int i = 0; i < emailPrefix.length(); i++) {
            // If email prefix respects all conditions, change result to true
            if (isValidPrefixChar(emailPrefix.charAt(i))) {
                result = true;
            } else {
                result = false;
                break;
            }
        }
    }
    return result;
}

让我们看看您的列表:

  • 至少包含一个字符。
  • 它仅包含字母数字字符、下划线(‘_’)、句点(‘.’)和破折号(‘-’)。
  • 下划线、句点或破折号后必须始终跟一个或多个字母数字字符。
  • 第一个字符必须是字母数字。

正如你所说,1、2、4 很简单。这就是我会做的。如果不正确,我的第一行将检查长度和 return false。然后我会遍历字符。在循环内;

  • 设置布尔值 lastWasSpecial = false。
  • 检查它是否为合法字符(条件 2)
  • 如果索引 == 0,请检查它是否为字母数字(条件 4)
  • 如果是特色菜之一:
    • 如果设置了 lastWasSpecial,return false
    • 设置 lastWasSpecial = true;
  • 否则再次设置 lastWasSpecial = false

应该是大约 10 行 easily-readable 代码。

local-part

仅供参考,COMMERCIAL AT 符号 (@) 之前的地址部分称为 local-part

避免char

当遇到 BMP 之外的字符时,使用 char 的代码将会中断。作为 16 位值,char 不能表示大多数字符。

代码点

在处理单个字符时,请改用代码点。代码点是永久分配给 Unicode 中定义的 140,000 多个字符中的每一个的编号。

int[] codePoints = localPart.codePoints().toArray() ;

定义一个数组、列表或一组您可接受的标点字符。

int codePoint = "-".codePointAt( 0 ) ;  // Annoying zero-based index counting. 

要验证遇到的每个标点符号后跟一个 letter/digit,首先要确保标点符号不是最后一个字符。如果不是,则在数组中查找以下代码点。测试该代码点是否 is a letter or digit.

if( Character.isLetterOrDigit( codePoints[ i + 1 ] ) ) { … }

算法可以优化,但我尝试只更改一些行并使用相同的代码风格。我在评论里加了说明

public static boolean isAlphanumeric(char c) {
    return Character.isLetterOrDigit(c);
}

public static boolean isValidPrefixChar(char preChar) {
    char[] prefixChar = new char[]{'-', '.', '_'};

    for (int i = 0; i < prefixChar.length; i++) {
        if (prefixChar[i] == preChar) {
            return true;
        }
    }
    return false;
}

public static boolean isValidPrefix(String emailPrefix) {
    boolean result = false;

    // To check if first character is alphanumeric
    if (isAlphanumeric(emailPrefix.charAt(0)) && emailPrefix.length() > 1) {
        // this boolean is set to true when the next char has to be alphanumeric
        boolean nextHasToBeAlphaNumeric = false;

        // the for loop start from 1 because char 0 has been already checked
        for (int i = 1; i < emailPrefix.length(); i++) {
            // If email prefix respects all conditions, change result to true
            char character = emailPrefix.charAt(i);
            if (isValidPrefixChar(character)) {
                // the previous char is '.', '_', '-' then you cannot have two valid prefix char together
                if (nextHasToBeAlphaNumeric) {
                    result = false;
                    break;
                } else {
                    // the next char has to be alphanumeric
                    result = true;
                    nextHasToBeAlphaNumeric = true;
                }
            } else if (isAlphanumeric(character)) {
                result = true;
                nextHasToBeAlphaNumeric = false;
            } else {
                result = false;
                break;
            }
        }
    }
    return result;
}