3
\$\begingroup\$

I am helping a friend with her Java homework and I adapted a solution I used in a similar project of my own for this. It is supposed to use loose/generous regex to make sure an email entered matches the forms [email protected] or [email protected].

import java.util.Scanner;

public class App {

    Scanner scanner;

    public App() {
        this.scanner = new Scanner(System.in);
    }

    public static void main(String[] args) {
        App app = new App();

        app.GetUsername(true);
    }

    public void GetUsername(Boolean firstRun) {

        if(!firstRun) {
            System.out.println("The username you have entered was in an incorrect format. Must match [email protected]");
        } else {
            System.out.println("Please enter a username:");
        }

        String userInput = this.scanner.nextLine();

        UsernameCheck usernameCheck = new UsernameCheck(userInput);

        if(usernameCheck.isValid()) {
            System.out.println("Welcome, " + userInput + "!");
        } else {
            GetUsername(false);
        }
    }

}

UsernameCheck.java

import java.util.regex.*;

public class UsernameCheck {

    String username;

    public UsernameCheck(String username) {
        this.username = username;
    }

    public Boolean isValid() {
        return this.username.matches("[a-zA-Z0-9\\.]+@[a-zA-Z0-9\\-\\_\\.]+\\.[a-zA-Z0-9]{3}");
    }
}

I am most interested in hearing alternative solutions for re-prompting a user if the input they've given is invalid. This works in practice, but I am looking for the cleanest way to do it.

\$\endgroup\$
5
  • \$\begingroup\$ Note: Your RegEx is not going to admit (a great many possible) eMail addresses, and will admit invalid ones. (e.g. domain names cannot have _, TLD's can be other than 3 letters, TLD's cannot be 3 digits, and the left-hand-side is significantly more forgiving than your regex.) I know this isn't related to the question you're asking, but do be aware that John_Crichton%[email protected] (or .museum) is potentially valid while JSmith@foo_bar.999 is not. (let alone addresses like postmaster@[10.20.30.40]) \$\endgroup\$
    – BRPocock
    Commented Oct 30, 2013 at 22:18
  • \$\begingroup\$ try ("[a-z0-9!#$%&'*+/=?^_{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?") \$\endgroup\$
    – BRPocock
    Commented Oct 30, 2013 at 22:32
  • \$\begingroup\$ It was stated in the assignment that only the most well-known three letter TLDs would be checked for (.edu, .com, .org, etc). My main concern was the performence of the GetUsername() method. Is using the method within itself the best choice in this case? Should I just do while(username isn't valid) ? \$\endgroup\$
    – Stephen K
    Commented Oct 30, 2013 at 22:55
  • \$\begingroup\$ In practical terms, I doubt that a real user will enter enough invalid addresses to smash the stack limits before getting bored and aborting the program, but Java doesn't offer tail-call optimization, so you are always in danger with those sorts of algos. \$\endgroup\$
    – BRPocock
    Commented Oct 30, 2013 at 23:01
  • \$\begingroup\$ Shouldn't your friend do her own homework? How is she going to learn anything from you getting internet strangers to do it? \$\endgroup\$
    – Theoriok
    Commented Jan 21, 2019 at 9:19

1 Answer 1

2
\$\begingroup\$

My preference for accepting user input in a blocking loop like this might look more like:

  public enum Returned { OK, ERROR_EMAIL };

  // Long loop here to get valid user registration info.
  /* In a transactional/event-driven (GUI, web) app this would not be
   * a loop, but with a blocking/modal (tty) program it works. */

  UserInfo u = new UserInfo ();
  // loop until you get valid one(s)
  while (!u.isReady()) {

    // for each field that must be validated, prompt and try to set it
    if (u.getName () == null) {
        System.out.print ("Enter an eMail address:");
        String entered = scanner.nextLine ();

        Returned settingName = u.setName (entered);

        // check each object's validity and report errors
        switch (settingName) {
            case Returned.OK:
                  System.out.println ("OK.");
                  break;
            case Returned.ERROR_EMAIL:
                  System.out.println ("That does not look like a valid eMail address.");
                  break;
             // No "default": if you add new error types later, you can handle them here.
             // The compiler will issue a warning about unhandled enum cases
        }
     }
  }

…and then in class UserInfo …

  final static Pattern rfc2822 = Pattern
                            .compile ("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?");

  public Returned setName (final String address)
  {
       Returned valid = isValid(address);
       if (Returned.OK == valid) { this.name = address; }
       return valid;
  }

  public boolean isValid (final String address)
  {
         return ( (rfc2822.matcher(address).matches ())
                  ? Returned.OK : Returned.ERROR_EMAIL );
         // you could also check for a valid MX record for the domain part…
  }

  public boolean isReady () {
         return name != null; // and whatever else
  }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.