TL;DR
The regex is:
^(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*(?: (?:(?:e|y|de(?:(?: la| las| lo| los))?|do|dos|da|das|del|van|von|bin|le) )?(?:(?:(?:d'|D'|O'|Mc|Mac|al\-))?(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+|(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*))+(?: (?:Jr\.|II|III|IV))?$
According to the page of regex Unicode, the \p{IsLatin}
is supported by Java, C#, PHP, Perl and Ruby. You can test this regular expression on the regexplanet.
Detailed explanation
First, let’s set some rules for full names:
- At least two names (although there are people who only have one name, such as Emperor Akihito of Japan, but let’s leave them out).
- Exactly a space separating names.
- The same name or surname may be composed, separated by hyphen. There may be more than one hyphen (e.g.: "Louis Auguste of Saxe-Coburg and Gotha")
- Accepting accents.
- The initial of each word must be uppercase and the other lower case letters.
- Must accept entirely minuscule conjunctions.
- Last names as "O'Brian", "d'Alembert" and "Mcdonald" must be accepted.
- There cannot be two or more conjunctions in a row (e.g.: "of the"). Meanwhile "María Antonieta de las Nieves" is a valid name, so the conjunction can be composed.
- Names and surnames (but not conjunctions) must have at least two letters.
- Apostrophes between letters are allowed (e.g.: "Samuel Eto'o"). But they can’t be at the beginning or the end of the word and they can’t be consecutive.
- Some names such as "Martin Luther King Jr." and "William Henry Gates III" has suffixes.
To do this with regex, we will stipulate the following:
The conjunctions are "and", "y", "of", "lo", "de los", "de la", "of las", "of", "of", "of", "of", "del", "van", "von", "bin" and "le".
Surnames can be prefixed with "d'", "D'", "The'", "Mc", "Mac" or "al-".
The suffixes are "Jr.", "II", "III" and "IV".
So the structure of the name would be this:
NOME-COMPLETO := PRENOME (espaço [CONJUNÇÃO espaço] SOBRENOME)+ (espaço SUFIXO)?
SOBRENOME := (PREFIXO)? NOME | PRENOME
PRENOME := NOME ("-" NOME)*
NOME := MAIÚSCULA (("'")? MINÚSCULA)+
PREFIXO := "d'" | "O'" | "Mc" | "Mac" | "al-"
SUFIXO = "Jr." | "II" | "III" | "IV"
CONJUNÇÃO := "e" | "y" | "de" (" lo" | " los" | " la" | " las")? | "do" | "dos" | "da" | "das" | "del" | "van" | "von" | "bin" | "le"
MAIÚSCULA := [\p{Lu}&&[\p{IsLatin}]]
MINÚSCULA := [\p{Ll}&&[\p{IsLatin}]]
That rule [\p{Lu}&&[\p{IsLatin}]]
is responsible for recognizing a character at the intersection of the set of uppercase letters (\p{Lu}
) and Latin characters (\p{IsLatin}
). Therefore, this also accepts capitalized Latin characters. The (\p{Ll}
) is for lowercase letters. See more about character classes in this other answer of mine and also at this link.
The above rule set can be read as a context-free grammar. However, it can be reduced to a regular expression, since there are no recursive rules in it. To do this, just replace the rules that are below the above rules.
However, how to build this regex manually is a boring, laborious, very error-prone process and the resulting regex is a monstrosity, especially if you have to change something from time to time, I made a program that builds the corresponding regex and also tests it with several different names. Here is the program (in Java):
Building and testing the regex
import java.util.regex.Pattern;
import java.util.StringJoiner;
class TesteRegex {
private static final String MAIUSCULA = "(?:[\\p{Lu}&&[\\p{IsLatin}]])";
private static final String MINUSCULA = "(?:[\\p{Ll}&&[\\p{IsLatin}]])";
private static final String PREFIXO = choice("d'", "D'", "O'", "Mc", "Mac", "al\\-");
private static final String SUFIXO = choice("Jr\\.", "II", "III", "IV");
private static final String CONJUNCAO = choice("e", "y", "de" + opt(choice(" la", " las", " lo", " los")), "do", "dos", "da", "das", "del", "van", "von", "bin", "le");
private static final String NOME = MAIUSCULA + plus(opt("'") + MINUSCULA);
private static final String PRENOME = NOME + star("\\-" + NOME);
private static final String SOBRENOME = choice(opt(PREFIXO) + NOME, PRENOME);
private static final String NOME_COMPLETO = "^" + PRENOME + plus(" " + opt(CONJUNCAO + " ") + SOBRENOME) + opt(" " + SUFIXO) + "$";
private static String opt(String in) {
return "(?:" + in + ")?";
}
private static String plus(String in) {
return "(?:" + in + ")+";
}
private static String star(String in) {
return "(?:" + in + ")*";
}
private static String choice(String... in) {
StringJoiner sj = new StringJoiner("|", "(?:", ")");
for (String s : in) {
sj.add(s);
}
return sj.toString();
}
private static final Pattern REGEX_NOME = Pattern.compile(NOME_COMPLETO);
private static final String[] NOMES = {
"Maria Silva",
"Pedro Carlos",
"Luiz Antônio",
"Albert Einstein",
"João Doria",
"Barack Obama",
"Friedrich von Hayek",
"Ludwig van Beethoven",
"Jeanne d'Arc",
"Saddam Hussein al-Tikriti",
"Osama bin Mohammed bin Awad bin Laden",
"Luís Inácio Lula da Silva",
"Getúlio Dornelles Vargas",
"Juscelino Kubitschek de Oliveira",
"Jean-Baptiste le Rond d'Alembert",
"Pierre-Simon Laplace",
"Hans Christian Ørsted",
"Joseph Louis Gay-Lussac",
"Scarlett O'Hara",
"Ronald McDonald",
"María Antonieta de las Nieves",
"Pedro de Alcântara Francisco António João Carlos Xavier de Paula Miguel Rafael Joaquim José Gonzaga Pascoal Cipriano Serafim",
"Luís Augusto Maria Eudes de Saxe-Coburgo-Gota",
"Martin Luther King Jr.",
"William Henry Gates III",
"John William D'Arcy",
"John D'Largy",
"Samuel Eto'o",
"Åsa Ekström",
"Gregor O'Sulivan",
"Ítalo Gonçalves"
};
private static final String[] LIXOS = {
"",
"Maria",
"Maria-Silva",
"Marcos E",
"E Marcos",
"Maria Silva",
"Maria Silva ",
" Maria Silva ",
"Maria silva",
"maria Silva",
"MARIA SILVA",
"MAria Silva",
"Maria SIlva",
"Jean-Baptiste",
"Jeanne d' Arc",
"Joseph Louis Gay-lussac",
"Pierre-simon Laplace",
"Maria daSilva",
"Maria~Silva",
"Maria Silva~",
"~Maria Silva",
"Maria~ Silva",
"Maria ~Silva",
"Maria da da Silva",
"Maria da e Silva",
"Maria de le Silva",
"William Henry Gates iii",
"Martin Luther King, Jr.",
"Martin Luther King JR",
"Martin Luther Jr. King",
"Martin Luther King Jr. III",
"Maria G. Silva",
"Maria G Silva",
"Maria É Silva",
"Maria wi Silva",
"Samuel 'Etoo",
"Samuel Etoo'",
"Samuel Eto''o"
};
private static void testar(String nome) {
boolean bom = REGEX_NOME.matcher(nome).matches();
System.out.println("O nome [" + nome + "] é bom? " + (bom ? "Sim." : "Não."));
}
public static void main(String[] args) {
System.out.println("Regex: " + NOME_COMPLETO);
System.out.println();
System.out.println("Esses nomes devem ser bons:");
for (String s : NOMES) {
testar(s);
}
System.out.println();
System.out.println("Esses nomes devem ser ruins:");
for (String s : LIXOS) {
testar(s);
}
}
}
This program builds regex using no-catch groups ((?: ... )
), zero-or-more times operator (*
), one-or-more times operator (+
), one-time or-no-time operator (?
), string start (^
) and end of string ($
).
See here the program working on ideone. Here is the exit from this program:
Regex: ^(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*(?: (?:(?:e|y|de(?:(?: la| las| lo| los))?|do|dos|da|das|del|van|von|bin|le) )?(?:(?:(?:d'|D'|O'|Mc|Mac|al\-))?(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+|(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*))+(?: (?:Jr\.|II|III|IV))?$
Esses nomes devem ser bons:
O nome [Maria Silva] é bom? Sim.
O nome [Pedro Carlos] é bom? Sim.
O nome [Luiz Antônio] é bom? Sim.
O nome [Albert Einstein] é bom? Sim.
O nome [João Doria] é bom? Sim.
O nome [Barack Obama] é bom? Sim.
O nome [Friedrich von Hayek] é bom? Sim.
O nome [Ludwig van Beethoven] é bom? Sim.
O nome [Jeanne d'Arc] é bom? Sim.
O nome [Saddam Hussein al-Tikriti] é bom? Sim.
O nome [Osama bin Mohammed bin Awad bin Laden] é bom? Sim.
O nome [Luís Inácio Lula da Silva] é bom? Sim.
O nome [Getúlio Dornelles Vargas] é bom? Sim.
O nome [Juscelino Kubitschek de Oliveira] é bom? Sim.
O nome [Jean-Baptiste le Rond d'Alembert] é bom? Sim.
O nome [Pierre-Simon Laplace] é bom? Sim.
O nome [Hans Christian Ørsted] é bom? Sim.
O nome [Joseph Louis Gay-Lussac] é bom? Sim.
O nome [Scarlett O'Hara] é bom? Sim.
O nome [Ronald McDonald] é bom? Sim.
O nome [María Antonieta de las Nieves] é bom? Sim.
O nome [Pedro de Alcântara Francisco António João Carlos Xavier de Paula Miguel Rafael Joaquim José Gonzaga Pascoal Cipriano Serafim] é bom? Sim.
O nome [Luís Augusto Maria Eudes de Saxe-Coburgo-Gota] é bom? Sim.
O nome [Martin Luther King Jr.] é bom? Sim.
O nome [William Henry Gates III] é bom? Sim.
O nome [John William D'Arcy] é bom? Sim.
O nome [John D'Largy] é bom? Sim.
O nome [Samuel Eto'o] é bom? Sim.
O nome [Åsa Ekström] é bom? Sim.
O nome [Gregor O'Sulivan] é bom? Sim.
O nome [Ítalo Gonçalves] é bom? Sim.
Esses nomes devem ser ruins:
O nome [] é bom? Não.
O nome [Maria] é bom? Não.
O nome [Maria-Silva] é bom? Não.
O nome [Marcos E] é bom? Não.
O nome [E Marcos] é bom? Não.
O nome [Maria Silva] é bom? Não.
O nome [Maria Silva ] é bom? Não.
O nome [ Maria Silva ] é bom? Não.
O nome [Maria silva] é bom? Não.
O nome [maria Silva] é bom? Não.
O nome [MARIA SILVA] é bom? Não.
O nome [MAria Silva] é bom? Não.
O nome [Maria SIlva] é bom? Não.
O nome [Jean-Baptiste] é bom? Não.
O nome [Jeanne d' Arc] é bom? Não.
O nome [Joseph Louis Gay-lussac] é bom? Não.
O nome [Pierre-simon Laplace] é bom? Não.
O nome [Maria daSilva] é bom? Não.
O nome [Maria~Silva] é bom? Não.
O nome [Maria Silva~] é bom? Não.
O nome [~Maria Silva] é bom? Não.
O nome [Maria~ Silva] é bom? Não.
O nome [Maria ~Silva] é bom? Não.
O nome [Maria da da Silva] é bom? Não.
O nome [Maria da e Silva] é bom? Não.
O nome [Maria de le Silva] é bom? Não.
O nome [William Henry Gates iii] é bom? Não.
O nome [Martin Luther King, Jr.] é bom? Não.
O nome [Martin Luther King JR] é bom? Não.
O nome [Martin Luther Jr. King] é bom? Não.
O nome [Martin Luther King Jr. III] é bom? Não.
O nome [Maria G. Silva] é bom? Não.
O nome [Maria G Silva] é bom? Não.
O nome [Maria É Silva] é bom? Não.
O nome [Maria wi Silva] é bom? Não.
O nome [Samuel 'Etoo] é bom? Não.
O nome [Samuel Etoo'] é bom? Não.
O nome [Samuel Eto''o] é bom? Não.
Note that regex has accepted all the names it should accept and rejected all those it should reject. The regex produced is:
^(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*(?: (?:(?:e|y|de(?:(?: la| las| lo| los))?|do|dos|da|das|del|van|von|bin|le) )?(?:(?:(?:d'|D'|O'|Mc|Mac|al\-))?(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+|(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+(?:\-(?:[\p{Lu}&&[\p{IsLatin}]])(?:(?:')?(?:[\p{Ll}&&[\p{IsLatin}]]))+)*))+(?: (?:Jr\.|II|III|IV))?$
It would not be better to apply titled case to the backend?
Maria Silva
it’s simple ever thought when it’s something likeMaria da Silva e Silva
?– rray
@rray, how could I do that? I could post an answer with an example?
– R.Santos
@Sergio killed the riddle. The asterisk point giving the problem
– Jefferson Quesado
@Jeffersonquesado I saw, but the question raised by rray makes sense
– R.Santos
Yeah, it makes total sense. And you can still get foreign names like
McFarlene
that has mixed lowercase and uppercase. But it depends on your data universe how to treat– Jefferson Quesado
But I’m not finding an example of how to use
titled case
to test, if you have an example to give– R.Santos
@danieltakeshi but what rray quoted would be this example of name
Maria da Silva e Silva
and so in his expression also does not catch– R.Santos
What about names with accents or with more than two components such as "Getúlio Dornelles Vargas"?
– Victor Stafusa
@Exact victorstafusa, though I haven’t found any example of how to meet that need, you would have some example?
– R.Santos
What about "Juscelino Kubitschek de Oliveira"? Conjunctions can also be tiny. It turns out you have to define what it is you want. You want to validate if the entry is a valid full name?
– Victor Stafusa
The very nice link to help you develop new patterns... https://www.piazinho.com.br/ed5/exemplos.html#163
– Elias de Oliveira