regex 解析西班牙语姓氏

iswrvxsc  于 2023-01-10  发布在  其他
关注(0)|答案(3)|浏览(153)

西班牙姓氏由三部分组成:

  • 父姓,
  • 可选的母亲姓名,
  • 可选配偶的父姓。

这三个部分中的每一个都是一个单独的词,前面可以有“德”、“德尔”、“德拉”、“德洛斯”或“德拉斯”。这些前缀中的每一个都以大写字母开头,每个部分只能有一个前缀。配偶的父姓与其余部分之间用“德”字隔开(没有大写字母)。
因此,有效的姓氏为:

  • 佩雷斯
  • 佩雷斯·德莱昂
  • 洛佩斯德洛佩斯
  • 德拉奥卡奥尔多涅斯
  • 卡斯蒂略拉米雷斯德瓦莱

我可以用这个正则表达式来解析这些名称:

^((?:De |Del |De La |De Los |De Las )?\w+)?( (?:De |Del |De La |De Los |De Las )?\w+)?( de (?:De |Del |De La |De Los |De Las )?\w+)?$

1.)这个难看的正则表达式可以简化吗?
2.)当父姓和母姓相同时,在它们之间插入“y”,所以“López y Lópey de De León”和“Pérez y Pérez”都有效,但“López y Pérez”和“Gómez y de Gómez”无效,我该如何描述这种情况?
非常感谢。

eqoofvh9

eqoofvh91#

确切的答案取决于您使用的编程语言和/或正则表达式引擎,但对于大多数实现,您应该能够执行以下操作:
(1.)创建一个单独的正则表达式,匹配名称的单个部分,然后将其包含在最终的正则表达式中,例如,在Perl中:

my $name1 = qr/(?:De |Del |De La |De Los |De Las )?\w+/;
my $name2 = qr/^($name1)( $name1)?( de $name1)?$/;

(我假设您在第一次捕获后不需要?,否则您将匹配空字符串。)$name2就是要匹配的正则表达式。
(2.)严格地说,正确的计算机理论正则表达式不能测试出现在字符串中一个点的任意子串是否也出现在另一个点。然而,大多数正则表达式实现(例如,Perl兼容的“正则表达式”)实际上比真实的的正则表达式引擎支持更多的功能,所以你可以使用如下反向引用:

my $name2 = qr/^(?:($name1)( $name1)?|($name1) y \3)(de $name1)?$/;

在PCRE中,\3匹配的字符串与第三个(...)组匹配的字符串完全相同。如果由于某种原因无法使用反向引用,则唯一的选择是使用如下正则表达式:

my $name2 = qr/^(?:($name1)( $name1)?|($name1) y ($name1))(de $name1)?$/;

然后,如果在匹配之后定义了$3$4,则测试它们是否相等(注意,上述两个方法都允许“López López”这样的名称不带“y”;如果你想禁止这些,那就有点难了。)

h4cxqtbf

h4cxqtbf2#

下面是我的尝试,它似乎与给出的例子一起工作:

public class Foo {

    public static void main(String[] args) throws Exception {
        System.out.println(new SpanishName("Pérez"));
        System.out.println(new SpanishName("Pérez De León"));
        System.out.println(new SpanishName("López de López"));
        System.out.println(new SpanishName("De La Oca Ordóñez"));
        System.out.println(new SpanishName("Castillo Ramírez de Del Valle"));
        System.out.println(new SpanishName("López y López de De León"));
        System.out.println(new SpanishName("Pérez y Pérez"));

        // System.out.println(new SpanishName("López y Pérez")); - Throws IAE
        // System.out.println(new SpanishName("Gómez y de Gómez")); - Throws IAE
    }

    public static class SpanishName {

        private final String paternal;
        private final String maternal;
        private final String spousePaternal;

        private static final Pattern NAME_REGEX = Pattern
                .compile("^([\\p{Ll}\\p{Lu}]+?)(?:\\s([\\p{Ll}\\p{Lu}]+?))?(?:\\s([\\p{Ll}\\p{Lu}]+?))?$");

        public SpanishName(String str) {
            str = stripJoinWords(str);
            str = removeYJoin(str);
            final Matcher matcher = NAME_REGEX.matcher(str);
            if (str.contains(" y ") || !matcher.matches()) {
                throw new IllegalArgumentException(String.format("'%s' is not a valid Spanish name", str));
            } else {
                paternal = matcher.group(1);
                maternal = matcher.group(2);
                spousePaternal = matcher.group(3);
            }
        }

        private String removeYJoin(final String str) {
            return str.replaceFirst("^([\\p{Ll}\\p{Lu}]+?) y \\1", "$1 $1");
        }

        private String stripJoinWords(final String str) {
            return str.replaceAll("(?<!\\sy\\s)[Dd]e(?:l| La| Los| Las)?\\s", "");

        }

        @Override
        public String toString() {
            return String.format("paternal = %s, maternal = %s, spousePaternal = %s", paternal, maternal,
                    spousePaternal);
        }
    }
}
9w11ddsr

9w11ddsr3#

与使用正则表达式不同,有一个服务在这方面做得非常出色:https://www.nameapi.org/en/demos/name-parser/。它是开源的,但是它没有使用正则表达式,而是从电话簿中收集数据,以及一组非常复杂的规则。

相关问题