【问题标题】:Sorting UTF-8 input对 UTF-8 输入进行排序
【发布时间】:2018-08-19 06:55:39
【问题描述】:

我需要对文件中的行进行排序,保存为 UTF-8。这些行可以以西里尔字母或拉丁字符开头。我的代码在西里尔字母上运行错误。

sub sort_by_default  {
    my @sorted_lines = sort {
        $a <=> $b
          ||
        fc( $a) cmp fc($b)
     } @_;
}

【问题讨论】:

  • 如何打开文件?你是怎么看台词的?
  • 拉丁语和西里尔语单词的顺序是什么?
  • open(FILE, "$address") or die "Can't open file: $!\n";我的@file = ;关闭(文件);
  • 拉丁文与我的行按字母顺序排序。
  • 问题演示的其余部分在哪里???你有什么问题???

标签: perl utf-8


【解决方案1】:

cmpsort 一起使用无法解决这个问题;它没有编码的概念,只是按代码点、逐个字符进行比较,在许多语言中都有惊喜。使用Unicode::Collate 更多信息请参见this post,更多信息请参见this post by tchrist 和此perl.com article

另一个问题是在 utf8 中正确读取(解码)输入和写入(编码)输出。确保处理标准流上的数据的一种方法是通过open pragma,您可以使用它设置“层”,以便在读取/写入数据时对输入和输出进行解码/编码。

一个例子

use warnings;
use strict;
use feature 'say';

use Unicode::Collate;

use open ":std", ":encoding(UTF-8)";

my $file = ...;

open my $fh, '<', $file or die "Can't open $file: $!";
my @lines = <$fh>;
chomp @lines;

my $uc  = Unicode::Collate->new();
my @sorted = $uc->sort(@lines);

say for @sorted;

模块的cmp方法可用于单独比较(如果数据 例如,在一个复杂的数据结构中,而不仅仅是一个扁平的行列表)

my @sorted = map { $uc->cmp($a, $b) } @data;

其中$a$b 需要适当设置,以便从@data 中提取要比较的内容。

如果您在源中有 utf8 数据,则需要 use utf8,而如果您通过其他渠道(包括来自 @ARGV)接收 utf8,则可能需要手动 Encode::decode 这些字符串。

有关详细信息,请参阅链接的帖子(及其中的链接)和文档。有关更全面的信息,请参阅此perlmonks post。有关自定义排序,请参阅此 Effective Perler article


示例:通过代码点比较 ä > b 而接受的德语订单是 ä b

perl -MUnicode::Collate -wE'use utf8; binmode STDOUT, ":encoding(UTF-8)"; 
    @s = qw(ä b); 
    say join " ", sort { $a cmp $b } @s;             #-->  b ä
    say join " ", Unicode::Collate->new->sort(@s);   #-->  ä b
'

所以我们需要使用Unicode::Collate(或自定义排序例程)。

【讨论】:

  • cmpsort 一起使用无法解决这个问题;它没有编码概念,只是逐个字符进行比较”只要字符串未编码,这正是您想要的。
  • @Borodin 但是对于来自 ascii 以外的编码的字符串会导致错误的排序?我认为链接的帖子是这个问题的一个很好的例子。
  • 要注意的主要事情是所有 Unicode graphemes 应该被规范化为 composeddecomposed (即作为单个字符或作为基本字符后跟一个组合标记)。我看不出原始编码会如何影响它。
【解决方案2】:

要打开保存为 UTF-8 的文件,请使用适当的层:

open my $FH, '<:encoding(UTF-8)', 'filename' or die $!;

不要忘记为输出设置相同的层。

#! /usr/bin/perl
use warnings;
use strict;

binmode *DATA, ':encoding(UTF-8)';
binmode *STDOUT, ':encoding(UTF-8)';
print for sort <DATA>;

__DATA__
Борис
Peter
John
Владимир

【讨论】:

    【解决方案3】:

    在 Perl 中正确处理 UTF-8 的关键是确保 Perl 知道某个信息的来源或目的地是 UTF-8。根据您获取或获取信息的方式,这样做会有所不同。如果 UTF-8 来自输入文件,则打开文件的方式是:

    open( my $fh, '<:encoding(UTF-8)', "filename" ) or die "Cannot open file: $!\n";
    

    如果您要在脚本源中使用 UTF-8,请确保您有:

    use utf8;
    

    在脚本的开头。

    如果您要从 STDIN 获取 UTF-8 字符,请在脚本开头使用:

    binmode(STDIN, ':encoding(UTF-8)');
    

    对于STDOUT 使用:

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

    另外,请务必阅读 UTF-8 vs. utf8 vs. UTF8 以了解每个编码名称之间的区别。 utf8UTF8 将允许有效的 UTF-8 和无效的 UTF-8(根据第一个 UTF-8 提议标准)并且不会抱怨无效的代码点。 UTF-8 将允许有效的 UTF-8,但不允许无效的代码点组合;它是utf-8-strict 的简称。您也可以阅读问题How do I sanitize invalid UTF-8 in Perl?

    最后,按照@zdim 的建议,你可以在脚本的开头使用:

    use open ':encoding(UTF-8)';
    

    以及here 所述的其他变体。这将为所有未明确指定层的open 指令设置编码层。

    【讨论】:

    • 或者您可以将open pragma 用于所有标准流
    猜你喜欢
    • 2019-04-09
    • 2020-03-16
    • 2010-11-02
    • 2015-03-20
    • 2011-12-17
    • 2011-12-08
    • 2014-11-05
    • 2015-05-23
    • 2023-03-18
    相关资源
    最近更新 更多