首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将姓名列表分成:"FirstName {TAB} Lastname“对

将姓名列表分成:"FirstName {TAB} Lastname“对
EN

Stack Overflow用户
提问于 2012-01-03 21:07:20
回答 1查看 126关注 0票数 0

如果你想转换/翻译下面的代码行,是否有特定的库、算法或技术(除了使用正则表达式)可供使用。

代码语言:javascript
运行
复制
"Acme Corporation Inc., John, Doe, F."
"Smith, Allen, Smith,Susan"
"Marshall, J., L., Johnson, H., Caruso, D., Jones, J."
"Stein, Harry, Joan, and Mike"

应将这些行转换为包含以下内容的文本:

代码语言:javascript
运行
复制
Acme {TAB} Corporation
Doe {TAB} John
Smith {TAB} Allen
Smith {TAB} Susan
Marshall {TAB} J.
Johnson {TAB} H.
Caruso {TAB} D.
Jones {TAB} J.
Stein {TAB} Harry
Stein {TAB} Joan
Stein {TAB} Mike

原文只包含专有名称和中间首字母(D.或J)。除了偶尔用“和”分隔同名的同级,与上面最后一行原文相同。

另外,这是否被认为是“命名实体识别”,或者这个过程还有其他的技术名称?

理想情况下,我希望使用Ruby/Python/Perl/PHP等语言编写的代码或算法可以进行这种转换。

有什么想法吗?提前谢谢。

EN

回答 1

Stack Overflow用户

发布于 2012-01-03 23:12:23

这几乎是可行的:

代码语言:javascript
运行
复制
#!/usr/bin/env perl
use strict;
use warnings;

my $tok = undef;
my @pairs = ();
my $looking_for = 'surname';

sub parse_line_to_words($){
    my $l = shift;
    my @words;
    my $word = '';
    my $start = 1;

    # remove trailing newlines
    chomp $l;
    if(index($l, '"', -1) != -1){
            # remove trailing quotation mark.
            chop $l;
    }
    foreach my $c (split//,$l){
            if($c eq '"'){
                    if($#words == -1){
                            # skip leading quotation marks
                            next;
                    }
            }

            if($c eq ','){
                    push(@words, $word);
                    $word = '';
                    $start = 1;
            } else{
                    if($start && $c eq ' '){
                            next;
                    } else{
                            $start = 0;
                    }
                    $word .= $c;
            }
    }
    if($word ne ''){
            push(@words, $word);
    }
    return @words;
}
sub peek_and(@){
    foreach my $word (@_){
            return 1 if $word eq 'and'
    }
    return 0;
}
sub split_and(@){
    my @copy;
    foreach my $word (@_){
            if(index($word, 'and ', 0) != -1){
                    my $i = index($word, 'and ', 0) + 4;
                    push(@copy, substr($word, 0, $i - 1));
                    push(@copy, substr($word, $i));
            } else{
                    push(@copy, $word);
            }
    }
    return @copy;
}
sub count_spaces($){
    my $w = shift;
    my $s=0;
    for(my $p = index($w, ' ', 0); $p != -1; $p=index($w, ' ', $p+1), $s++) {}
    return $s;
}
sub found($$$){
    my $pairs = shift;
    push(@{$pairs}, {'surname' => shift, 'firstname' => shift});
}
while(<>){
    chomp;
    my $line = $_;
    my @words = parse_line_to_words($line);
    @words = split_and(@words);
    my $line_has_and = peek_and(@words);
    foreach my $word (@words){
            my $spaces = count_spaces($word);

            if($looking_for eq 'surname'){
                    if(index($word, '.', -1) != -1 && $spaces == 0){
                            # looks like an initial to me, skip it
                    } else{
                            if($spaces > 0){
                                    # multi-word token; must be corporation name
                                    my($f, $l) = split(/ /, $word);
                                    found(\@pairs, $f, $l);
                            } else{
                                    $tok = $word;
                                    $looking_for = 'firstname';
                            }
                    }
            } elsif ($looking_for eq 'firstname'){
                    if($line_has_and){
                            # lastname, first1, ..., firstn and firstn+1
                            if($word ne 'and'){
                                    found(\@pairs, $tok, $word);
                            }
                    } else{
                            # lastname, f. or lastname, firstname
                            found(\@pairs, $tok, $word);
                            $looking_for = 'surname';
                    }
            }
    }
    $looking_for = 'surname'; # reset for new line
}

foreach my $p (@pairs){
    printf("%s\t%s\n", $p->{'surname'}, $p->{'firstname'});
}

给定样本输入的实际输出

代码语言:javascript
运行
复制
Acme    Corporation
John    Doe
Smith   Allen
Smith   Susan
Marshall        J.
Johnson H.
Caruso  D.
Jones   J.
Stein   Harry
Stein   Joan
Stein   Mike

讨论

我采用了以下启发式方法:

  • 应忽略行上的前导引号和尾部引号。
  • 可将每行标记为一系列逗号分隔值的单词。
  • 如果单词以空格字符开头,则应忽略这些字符。
  • 任何一对单词的第一个单词都是姓氏。第二个是名字(特殊情况除外)。如果一行中的一个单词以‘

’开头,则应特殊处理整个行,其中第一个单词是姓氏,其余是对应的名字。

如果一个姓氏包含的空格超过0个,则它是corporation

  • A公司名称的名称始终是两个空格分隔的单词,应视为姓氏,并且
  • 名称不包含空格。

最后,我使用“正则表达式”只是为了在空格上拆分公司名称;这可以用一个非正则表达式版本替代。

尽管如此,我仍然得到"John Doe“错误,因为它的名称在输入中是颠倒的。我想不出一个可靠的方法来检测这一点。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8712760

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档