【问题标题】:Capitalizing strings which contain accented characters将包含重音字符的字符串大写
【发布时间】:2013-10-24 04:15:08
【问题描述】:

我正在尝试为 perl webapp 中的名称大写寻找解决方案(使用 perl v5.10.1)。我最初想使用 Lingua::EN::NameCase,但发现重音字符存在一些问题。

我需要能够处理来自各种欧洲语言(爱尔兰语、法语、德语)的重音字符。

我在网上看到一些迹象表明 Lingua::EN::NameCase 应该适用于我的用例。例如,这个关于 perlmonks 的页面:http://www.perlmonks.org/?node_id=889135

这是我基于上述链接的测试代码:

#!/usr/bin/perl

use strict;
use warnings;
use Lingua::EN::NameCase;
use locale;
use POSIX qw(locale_h);

my $locale = 'en_FR.utf8';

setlocale( LC_CTYPE, $locale );

binmode DATA,   ':encoding(UTF-8)';
binmode STDOUT, ':encoding(UTF-8)';

while (my $original_name = <DATA>) {
    chomp $original_name;
    my $normalized_name = nc($original_name);
    printf "%30s L::EN::NC %30s UCFIRST %30s\n", $original_name, $normalized_name, xlc($original_name);
}

sub xlc {
    my $str = shift;
    $_ = lc( $str );
    return join q{} => ( map { ucfirst(lc($_)) } ( $str =~ m/(\W+|\w+)/g ) );
};

__DATA__
ÉTIENNE DE LA BOÉTIE
ÉMILIE DU CHÂTELET
HÉLÈNE CIXOUS
Seán Ó Hannracháín
Máire Ó hÓgartaigh

产生下面的输出。 L::EN::NC 和自定义 ucfirst(lc()) 解决方案都会产生不正确的结果(注意每个重音字符后面的大写字母)。这似乎是因为 perl 正则表达式在每个重音字符之前/之后匹配“单词边界”。我希望单词边界只匹配空格字符和非空格字符。

有人可以提出解决方案吗?

谢谢,

布赖恩。

  ÉTIENNE DE LA BOÉTIE L::EN::NC           éTienne de la BoéTie UCFIRST           ÉTienne De La BoÉTie
    ÉMILIE DU CHÂTELET L::EN::NC             éMilie du ChâTelet UCFIRST             ÉMilie Du ChÂTelet
         HÉLÈNE CIXOUS L::EN::NC                  HéLèNe Cixous UCFIRST                  HÉLÈNe Cixous
    Seán Ó Hannracháín L::EN::NC             SeáN ó HannracháíN UCFIRST             SeÁN ó HannrachÁíN
    Máire Ó hÓgartaigh L::EN::NC             MáIre ó HóGartaigh UCFIRST             MÁIre ó HÓGartaigh

【问题讨论】:

  • hwnd 发布的链接很有趣,但 $original_name 上的 utf8 标志 已设置:一切都已正确解码。
  • 确实如此。我对大写本身没有问题。 uc() 和 lc() 似乎在我发送给他们的任何字符串上都可以正常工作。问题是 L::EN::NC 似乎无法正确识别单词的开头,以便将该单词的第一个字母大写。 L::EN::NC 中的相关正则表达式是s{ \b (\w) }{\u$1}gox ;,它使用\b 来识别单词边界。对我来说\b 似乎将重音字符和非重音字符之间的任何变化识别为单词边界,这对我来说似乎是错误的。
  • Perl Unicode test on OS X fails on Debian 的可能副本——但我不太确定。 en_*.*-locale 根本不认为é\w 中。
  • @amon,感谢您的跟进。你指出我的问题似乎是同一个问题,并让我开始思考为什么某些语言环境不会将é 视为在\w 中。长话短说,将原始示例中的语言环境更改为 en_IEfr_FR 解决了原始问题。在另一个问题中对 perlre 的引用使我相信 use feature 'unicode_strings' 可能也解决了我的问题(通过将重音字符视为 \w 的一部分),但我没有足够新的 perl 来使用该功能。

标签: regex perl unicode capitalization


【解决方案1】:

如果你的数据是 UTF8,你应该把它解码成 perl 的内部编码:

    utf8::decode($original_name);
    my $normalized_name = nc($original_name);
    printf "%30s L::EN::NC %30s UCFIRST %30s\n", $original_name, $normalized_name, xlc($original_name);

【讨论】:

  • 谢谢博丹。我的数据确实是 UTF8 —— utf8::is_utf8( $original_name ) 返回 true。但是 utf8::decode() 并没有给我想要的输出。它确实改变了输出。因此,在我的原始示例中,L::EN::NC 不是给出“éTienne de la BoéTie”,而是现在给出“ÉTienne de la BoÉTie”。所以大写已经改变,但我仍然有中间字符串的虚假大写。
【解决方案2】:

Perl 5.10 太旧了;如果可以的话,你应该更新它。

接下来您会找到我用于类似情况的版本。 (在 perl 5.14.2 中测试)

#!/usr/bin/perl

use strict;
use warnings;
use utf8::all;

while (<DATA>) { chomp;
    printf "%30s ==> %30s\n", $_, xlc($_);
}

sub xlc { my $str = shift;
    $str =~ s/(\w+)/ucfirst(lc($1))/ge;
    $str =~ s/( L[ea]s?
               | Von
               | D[aeou]s?
               )\b
              /lc($1)/xge;
    return $str;
};

__DATA__
ÉTIENNE DE LA BOÉTIE
ÉMILIE DU CHÂTELET
HÉLÈNE CIXOUS
Seán Ó Hannracháín
Máire Ó hÓgartaigh

【讨论】:

  • 刚刚注意到我们几乎给出了相同的答案。但你是第一个。所以这是我的赞成票:)
【解决方案3】:

好的,我刚刚让你的脚本工作。这是我得到的输出:

      ÉTIENNE DE LA BOÉTIE L::EN::NC           Étienne de la Boétie UCFIRST           Étienne De La Boétie
        ÉMILIE DU CHÂTELET L::EN::NC             Émilie du Châtelet UCFIRST             Émilie Du Châtelet
             HÉLÈNE CIXOUS L::EN::NC                  Hélène Cixous UCFIRST                  Hélène Cixous
        Seán Ó Hannracháín L::EN::NC             Seán Ó Hannracháín UCFIRST             Seán Ó Hannracháín
        Máire Ó hÓgartaigh L::EN::NC             Máire Ó Hógartaigh UCFIRST             Máire Ó Hógartaigh

我不得不改变两件事:

  1. 我注释掉了 binmode 调用,因为我的 emacs 在我的系统上使用的任何编码都不需要它们。你的旅费可能会改变。如果您弄错了,您会看到有关未映射到 Unicode 或宽字符的字符的警告。

  2. 我更改了本地。你告诉它使用法国的英语语言环境。我不确定这是一个有效的语言环境。我选择了一个实际使用重音字符的本地人。

不幸的是,语言环境名称没有标准化,但以下语言环境对我有用:

my $locale = 'fr_FR.utf-8';

特别是,如果没有连字符,它就无法工作。

【讨论】:

    【解决方案4】:

    其实你只需要 utf8 pragma。

    use utf8;
    binmode STDOUT, ':utf8'; 
    
    while (my $name = <DATA>) {
        $name =~ s/(\w+)/ucfirst lc $1/eg;
        print $name;
    }
    
    __DATA__
    ÉTIENNE DE LA BOÉTIE
    ÉMILIE DU CHÂTELET
    HÉLÈNE CIXOUS
    Seán Ó Hannracháín
    Máire Ó hÓgartaigh
    

    我明白了:

    Étienne De La Boétie
    Émilie Du Châtelet
    Hélène Cixous
    Seán Ó Hannracháín
    Máire Ó Hógartaigh
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-10
      • 2011-09-27
      • 1970-01-01
      • 2014-09-11
      • 2018-09-10
      • 1970-01-01
      相关资源
      最近更新 更多