【问题标题】:convert a string with unicode characters to lower case将带有 unicode 字符的字符串转换为小写
【发布时间】:2013-08-29 10:11:49
【问题描述】:

问题说明 - 我正在处理一些数据文件。在那个数据转储中,我有一些字符串,其中包含字符的 unicode 值。字符可能是大写和小写。现在我需要对这个字符串做下面的处理。

1- 如果有 - , _ ) ( } { ] [ ' " 则删除它们。所有这些字符都以 Unicode 形式存在于字符串中,为 ($4-hexa-digits)

2- 所有大写字符都需要转小写(包括所有不同的unicode字符'Φ' -> 'φ', 'Ω' -> 'ω', 'Ž' -> 'ž')

3- 稍后我将使用这个最终字符串来匹配不同的用户输入。

问题详细描述--我有一些字符串,例如Buna$002C_Texas , Zamboanga_$0028province$0029 等等。

这里 $002C, $0028$0029 是 unicode 值,我正在使用下面将它们转换为它们的字符表示。

$str =~s/\$(....)/chr(hex($1))/eg;

$str =~s/\$(....)/pack 'U4', $1/eg;

现在我根据我的要求替换所有字符。然后我将字符串解码为 utf-8 以获取包括 unicode 在内的所有字符的小写,如下所示,因为 lc 直接不支持 unicode 字符。

$str =~ s/(^\-|\-$|^\_|\_$)//g;                        
$str =~ s/[\-\_,]/ /g;                                                                         
$str =~ s/[\(\)\"\'\.]|ʻ|’|‘//g;                                                                                       
$str =~ s/^\s+|\s+$//g;
$str =~ s/\s+/ /g;
$str = decode('utf-8',$str);
$str = lc($str);
$str = encode('utf-8',$str);

但是当 Perl 尝试解码字符串时,我遇到了错误。

Cannot decode string with wide characters at /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/Encode.pm line 173

此错误也很明显,如此处所述。 @http://www.perlmonks.org/?node_id=569402

现在我按照上面的 url 改变了我的逻辑。我在下面使用将 unicode 转换为字符表示。

$str =~s/\$(..)(..)/chr(hex($1)).chr(hex($2))/eg;

但是现在我没有得到字符表示。我得到了一些不可打印的字符。 那么当我不知道会有多少不同的 unicode 表示时如何处理这个问题。

【问题讨论】:

  • 您不需要转义_()"'。而破折号- 只是字符类括号[ ... ] 内的一个元字符。

标签: regex perl unicode


【解决方案1】:

您希望在进行转换之前对字符串进行解码,最好使用像:utf8 这样的 PerlIO 层。因为您在解码之前插入了转义的代码点,所以您的字符串可能已经包含多字节字符。请记住,Perl(似乎)对代码点进行操作,而不是字节。

所以我们要做的是:解码、取消转义、规范化、删除、大小写折叠:

 use strict; use warnings;
 use utf8;  # This source file holds Unicode chars, should be properly encoded
 use feature 'unicode_strings'; # we want Unicode semantics everywhere
 use Unicode::CaseFold; # or: use feature 'fc'
 use Unicode::Normalize;

 # implicit decode via PerlIO-layer
 open my $fh, "<:utf8", $file or die ...;
 while (<$fh>) {
   chomp;

   # interpolate the escaped code points
   s/\$(\p{AHex}{4})/chr hex $1/eg;

   # normalize the representation
   $_ = NFD $_;  # or NFC or whatever you like

   # remove unwanted characters. prefer transliterations where possible,
   # as they are more efficient:
   tr/.ʻ//d;
   s/[\p{Quotation_Mark}\p{Open_Punctuation}\p{Close_Punctuation}]//g;  # I suppose you want to remove *all* quotation marks?
   tr/-_,/   /;
   s/\A\s+//;
   s/\s+\z//;
   s/\s+/ /g;

   # finally normalize case
   $_ = fc $_

   # store $_ somewhere.
 }

您可能对 perluniprops 感兴趣,它是所有可用 Unicode 字符属性的列表,例如 Quotation_MarkPunct(标点符号)、Dash(破折号,如 - - -)、Open_Punctuation(括号如({[〈 和引号如„“) 等。

我们为什么要执行 unicode 规范化?一些字素(视觉字符)可以有多种不同的表示。例如,á 可以表示为“a 与急性”或“a”+“组合急性”。 NFC 试图将信息组合成一个代码点,而NFD 将这些信息分解成多个代码点。请注意,这些操作会更改字符串的长度,因为长度以代码点为单位。

在输出你分解的数据之前,最好重新分解一下。

为什么我们使用 fc 的大小写折叠而不是小写?两个小写字符可能是等价的,但不会比较相同,例如希腊小写 sigma:σς。案例折叠使这一点正常化。德语 ß 大写为两个字符序列 SS。因此,"ß" ne (lc uc "ß")。大小写折叠对此进行了规范化,并将ß 转换为ssfc("ß") eq fc(uc "ß")。 (但无论您做什么,您仍然可以享受土耳其数据带来的乐趣)。

【讨论】:

  • lc 仍然是小写的合适工具。 fc 仅用于执行不区分大小写的比较。
  • @ikegami 我知道这一点。然而,所有这些标准化都是为了 3 的既定目的而发生的。 … 匹配不同的用户输入
  • 那么fc 就完美了。
  • +1 用于放弃小写废话并进行折叠。
猜你喜欢
  • 1970-01-01
  • 2016-06-18
  • 1970-01-01
  • 2023-03-31
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多