2009-08-28 3 views
11

私は文字列"re\x{0301}sume\x{0301}"(これは次のように表示されます:ré sumé)を逆にして"e\x{0301}muse\x{0301}r"(é musé r)にします。 Perlのreverseは、別の文字として"\x{0301}"のような文字を扱うため、使用できません。したがって、"\x{0301}emus\x{0301}er"(́ emuś er)になります。どのように文字列を逆にすることができますが、結合文字を尊重しますか?Perlで結合文字を含む文字列をどのように逆にすることができますか?

あなたが \X special escapeが戻っ join、その後、書記素のリストを逆に、(それらの間の空の文字列で)書記素のリストを作成する splitでそれらを(非結合文字と、次の組み合わせのすべての文字に一致する)を使用することができます

答えて

8

最良の答えは、私はチャスの例を少し変更し、Unicode::GCStringを使用するas Sinan points out


です:

  • "ワイド文字の印刷中"の警告を避けるために、STDOUTのエンコードを設定してください。これは、基本的には微調整のカップルと同じことだ

  • は(明らかに、5.10の後に動作しませんので、私はそれを削除)splitに肯定先読みアサーション(なし、セパレータ保持モード)を使用してください。

    use strict; 
    use warnings; 
    
    binmode STDOUT, ":utf8"; 
    
    my $original = "re\x{0301}sume\x{0301}"; 
    my $wrong = reverse $original; 
    my $right = join '', reverse split /(\X)/, $original; 
    
    print <<HERE; 
    original: [$original] 
        wrong: [$wrong] 
        right: [$right] 
    HERE 
    
  • +0

    うわー。私はperlが好きですが、その分割式はかなり魔法です。私の最初の考えは "ブルートフォース"でした。分割が何をするかを行う関数を作りました - それぞれのエントリが論理的な文字を表す文字列のリストを返します。しかし、あなたはそのリスト(@xと呼ぶ)を得るが、幸いなことにjoin( ''、reverse(@x))の部分は明らかに続く。 – Roboprog

    +2

    Magical?どうして?それは副作用のない単なる正規表現であり、あなたが見るものだけを正確に行います。それが魔法だと思うなら、あなたはPerlの本当の黒い芸術を見たことがありません。あなたはそれを賢明と呼ぶかもしれませんが(私はそうしませんが)、それは魔法ではありません。これはおそらく今まで使ったことのないものです。 –

    +0

    私はPerl v5.12.4を使ってこの例を実行しようとしましたが、動作しませんでした。代わりに/(\ X)/を使用しました。 この回答はPerlの以前のバージョンでは機能しましたか? – Flimm

    12

    一緒:

    #!/usr/bin/perl 
    
    use strict; 
    use warnings; 
    
    my $original = "re\x{0301}sume\x{0301}"; 
    my $wrong = reverse $original; 
    my $right = join '', reverse split /(\X)/, $original; 
    print "original: $original\n", 
         "wrong: $wrong\n", 
         "right: $right\n"; 
    
    +1

    (私が最初であったように)書記との間に空の文字列がある理由については、それはだ:それはデータを使用していますこれはセパレータとして必要です。空の文字列は、2つのグラフェムの間にあるものです。結果にセパレータを含めることによってのみ、 "実"の結果、つまり空の文字列が混在したグラフェンを得ることができます。 'm // g 'を使ってグラフェンを捕捉する代わりに、代わりに' 'join' '、reverse $ original =〜/(\ X)/ g' –

    +2

    マイケルのコメントを明確にするあなたが分割する正規表現にメモリ括弧を使用すると、 "セパレータ保持モード"がトリガされます。あなたは分割している部分の間にあるものを取り戻します。あなたはそれを行う必要はありません。パターン(?= \ X)は余分なビットなしで同じことを行います。空の文字列は実際には小さな文字列にとってはそれほど重要ではありません。 –

    +0

    "セパレータ保持モード"を指摘するのは正しいです、ありがとう、それは参考になりました。ただし、(?= \ X)は等価ではありません。 が証明するために、これらの2つの例を考える: スプリット/(A)/、 "ABC" は(?= A)/、 "ABC" およびスプリット/(B + C)/分割と等価ではありません/、 "abbcd"はsplit /(?= b + c)/、 "abbcd" – Flimm

    0

    その他の回答には、うまく機能しない要素が含まれています。以下はPerl 5.12と5.14でテストされた実例です。 binmodeの指定に失敗すると、出力にエラー・メッセージが生成されます。スプリットで正のルックアヘッドアサーション(およびセパレータ保持モードなし)を使用すると、Macbookで出力が正しく表示されなくなります。あなたがUnicode::GCString使用することができます

    #!/usr/bin/perl 
    
    use strict; 
    use warnings; 
    use feature 'unicode_strings'; 
    
    binmode STDOUT, ":utf8"; 
    
    my $original = "re\x{0301}sume\x{0301}"; 
    my $wrong = reverse $original; 
    my $right = join '', reverse split /(\X)/, $original; 
    print "original: $original\n", 
         "wrong: $wrong\n", 
         "right: $right\n"; 
    
    2

    Unicodeを:: GCStringは、Unicode標準の附属書#29 [UAX#29]で定義された拡張書記素クラスタのシーケンスとしてUnicode文字列を扱います。

    #!/usr/bin/env perl 
    
    use utf8; 
    use strict; 
    use warnings; 
    use feature 'say'; 
    use open qw(:std :utf8); 
    
    use Unicode::GCString; 
    
    my $x = "re\x{0301}sume\x{0301}"; 
    my $y = Unicode::GCString->new($x); 
    my $wrong = reverse $x; 
    my $correct = join '', reverse @{ $y->as_arrayref }; 
    
    say "$x -> $wrong"; 
    say "$y -> $correct"; 
    

    出力: `split`が反転しているので、混同したものについては

    résumé -> ́emuśer 
    résumé -> émusér
    関連する問題