【问题标题】:Perl6: Capturing Windows newline in a string with regexPerl6:使用正则表达式捕获字符串中的 Windows 换行符
【发布时间】:2019-01-02 04:31:09
【问题描述】:

免责声明:我已在 PerlMonks 交叉发布此内容。

在 Perl5 中,我可以快速轻松地打印出 \r\n Windows 风格行尾的十六进制表示:

perl -nE '/([\r\n]{1,2})/; print(unpack("H*",$1))' in.txt
0d0a

要在 Unix 上创建一个以 Windows 结尾的文件,如果您想进行测试,请创建一个带有单行和行结尾的 in.txt 文件。然后:perl -ni -e 's/\n/\r\n/g;print' in.txt。 (或在 vi/vim 中,创建文件并执行 :set ff=dos)。

我在 Perl6 中尝试了很多东西来做同样的事情,但无论我做什么都无法让它工作。这是我最近的测试:

use v6;
use experimental :pack;

my $fn = 'in.txt';

my $fh = open $fn, chomp => False; # I've also tried :bin
for $fh.lines -> $line {
    if $line ~~ /(<[\r\n]>**1..2)/ {
        $0.Str.encode('UTF-8').unpack("H*").say;
    }
}

输出0a,同样:

/(\n)/
/(\v)/ 

首先,我什至不知道我是否正确使用了unpack() 或正则表达式。其次,如何在 P6 中捕获换行符的两个元素(\r\n)?

【问题讨论】:

  • 在 Perl5 中,我更喜欢sprintf("%v02X", $s)。适用于任何字符串,而不仅仅是字节字符串。

标签: regex raku


【解决方案1】:

Perl 6 会自动为您去掉行分隔符。这意味着当您尝试进行替换时它不存在。

如果有组合字符,Perl 6 也会创建合成字符。因此,如果您想要输入的基数 16 表示,请使用编码 'latin1' 或使用 $*IN 上返回 Buf 的方法。


此示例只是将 CRLF 附加到每一行的末尾。
(最后一行总是以0D 0A 结尾,即使它没有行终止符)

perl6 -ne 'BEGIN $*IN.encoding("latin1"); #`( basically ASCII )
    $_ ~= "\r\n";  #`( append CRLF )
    put .ords>>.fmt("%02X");'

您也可以关闭自动选择行为。

perl6 -ne 'BEGIN {
      $*IN.encoding("latin1");
      $*IN.chomp = False;
    };
    s/\n/\r\n/;
    put .ords>>.fmt("%02X");'

【讨论】:

    【解决方案2】:

    好的,所以我的目标是(很抱歉,我在发布问题时没有说清楚)是我想读取一个文件,捕获行尾,然后使用原始文件写回文件行结尾(而不是当前平台的结尾)。

    我现在得到了一个概念证明。我对 Perl 6 很陌生,所以代码可能不是很 p6-ish,但它确实可以满足我的需要。

    在 FreeBSD 上测试的代码:

        use v6;
        use experimental :pack;
    
        my $fn = 'in.txt';
        my $outfile = 'out.txt';
    
        # write something with a windows line ending to a new file
    
        my $fh = open $fn, :w;
        $fh.print("ab\r\ndef\r\n");
        $fh.close;
    
        # re-open the file 
    
        $fh = open $fn, :bin;
    
        my $eol_found = False;
        my Str $recsep = '';
    
        # read one byte at a time, or else we'd have to slurp the whole
        # file, as I can't find a way to differentiate EOL from EOF
    
        while $fh.read(1) -> $buf {
            my $hex = $buf.unpack("H*");
            if $hex ~~ /(0d|0a)/ {
                $eol_found = True;
                $recsep = $recsep ~ $hex;
                next;
            }
            if $eol_found {
                if $hex !~~ /(0d|0a)/ {
                    last;
                }
            }
        }
    
        $fh.close;
    
        my %recseps = (
            '0d0a' => "\r\n",
            '0d'   => "\r",
            '0a'   => "\n",
        );
    
        my $nl = %recseps<<$recsep>>;
    
        # write a new file with the saved record separator
    
        $fh = open $outfile, :w;
        $fh.print('a' ~ $nl);
        $fh.close;
    
        # re-read file to see if our newline stuck
    
        $fh = open $outfile, :bin;
    
        my $buf = $fh.read(1000);
        say $buf;
    

    输出:

    Buf[uint8]:0x<61 0d 0a>
    

    【讨论】:

    • 我会回去看介绍/文档,等我熟练了之后,用我的新知识再次测试。
    • 来自roastnewline.t 有用吗?
    • 绝对是!非常感谢。事实上,我什至没有想过要查看测试文件作为示例,但整个套件将成为一个很棒的学习工具。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    • 2012-04-11
    • 2023-01-17
    • 2011-10-05
    • 1970-01-01
    • 2016-12-15
    相关资源
    最近更新 更多