【问题标题】:How to “apply” backspace characters with perl如何使用 perl “应用”退格字符
【发布时间】:2015-08-03 09:15:07
【问题描述】:

我有一个包含多个退格字符 (^H) 的文件。我希望能够在 perl 中“应用”这些退格键。我找到了一些解决方案,但没有一个对我有用。 关键是这一行:

test>>M^H ^HManagement.^H^H^H^H^H^H^H^H^H^Hanagement.F^H ^HFiles.^H^H^H^H^Hiles.s^H ^Hs.^H ^Hc^H ^H^H ^Hscript.^H ^H^H^H^H^Hripts^H ^H^H ^H^H ^H^H ^H^H ^H^H ^H^H ^Hscripts.^H.s^H ^Hshow_file ^H^H^H^H^H^H^H^H^Hhow_file = transform_factory_to_running^M

结果应该是这样的:

test>>Management.Files.scripts.show_file = transform_factory_to_running^M

在 vi 中,我能够按照https://stackoverflow.com/a/1298728/2837411 中的建议正确转换文本。 但是 perl 解决方案,也建议在这个问题中:https://stackoverflow.com/a/1298970/2837411没有为我工作(使用$_):

s{([^\x08]+)(\x08+)}{substr$1,0,-length$2}eg;

这个的输出是:

test>>Management.Files.sscriptriptscripts.show_file = transform_factory_to_running^M

所有退格都消失了,但看起来好像其中一些退格被应用到另一个退格?!

【问题讨论】:

标签: regex perl non-printing-characters


【解决方案1】:

这只是在一个替换循环中完成

它反复删除行首的所有退格(无效)或非退格字符后跟一个退格(模拟删除前一个字符)

请注意,我必须在正则表达式模式中使用 \cH 而不是 \b,因为后者是此上下文中的单词边界锚

use strict;
use warnings;
use v5.10;

my $s = 'M^H ^HManagement.^H^H^H^H^H^H^H^H^H^Hanagement.F^H ^HFiles.^H^H^H^H^Hiles.s^H ^Hs.^H ^Hc^H ^H^H ^Hscript.^H ^H^H^H^H^Hripts^H ^H^H ^H^H ^H^H ^H^H ^H^H ^H^H ^Hscripts.^H.s^H ^Hshow_file ^H^H^H^H^H^H^H^H^Hhow_file = transform_factory_to_running^M';
$s =~ s/\^H/\b/g; # convert `^H` to backspace

1 while $s =~ s/(?:^|[^\cH])\cH//g;

say $s;

输出

Management.Files.scripts.show_file = transform_factory_to_running^M

更新

这是一个将字符串作为字符流处理的版本,类似于simbabque's 解决方案,但从左到右

基本上任何退格都会从$result 缓冲区的末尾删除一个字符(如果有一个字符要删除),而任何其他字符都只是简单地附加

输出与上面的代码相同

use strict;
use warnings;
use v5.10;

my $s = 'M^H ^HManagement.^H^H^H^H^H^H^H^H^H^Hanagement.F^H ^HFiles.^H^H^H^H^Hiles.s^H ^Hs.^H ^Hc^H ^H^H ^Hscript.^H ^H^H^H^H^Hripts^H ^H^H ^H^H ^H^H ^H^H ^H^H ^H^H ^Hscripts.^H.s^H ^Hshow_file ^H^H^H^H^H^H^H^H^Hhow_file = transform_factory_to_running^M';
$s =~ s/\^H/\b/g;

say apply_backspace_characters($s);

sub apply_backspace_characters {

  my $result;

  for my $c ( split //, shift ) {
    if ( $c eq "\b" ) {
      substr($result, -1) = '';
    }
    else {
      $result .= $c;
    }
  }

  $result;
}

【讨论】:

  • @simbabque:感谢修复
【解决方案2】:

这是一个非常明确的解决方案,可能不是最快的。但是,它完成了工作。

sub apply_backspace_characters {
    my $string = shift;

    # replace the ^H characters with one BS char
    $string =~ s/\^H/chr(8)/ge;

    my @output;
    my $backspace_count = 0; # keep track of how many BS we have seen in a row

    # iterate over string by char from the right
    foreach my $char ( reverse split //, $string ) {
        if ( $char eq chr(8) ) {
            # it's a backspace, increase counter and skip
            $backspace_count++;
            next;
        }
        if ($backspace_count) {
            # there are still backspaces on the 'stack', decrease counter and skip
            $backspace_count--;
            next;
        }
        # no backspaces left, keep this character and put at front
        # (because we are going backwards)
        unshift @output, $char;
    }

    return join '', @output;
}

say apply_backspace_characters(
    "test>>M^H ^HManagement.^H^H^H^H^H^H^H^H^H^Hanagement.F^H ^HFiles.^H^H^H^H^Hiles.s^H ^Hs.^H ^Hc^H ^H^H ^Hscript.^H ^H^H^H^H^Hripts^H ^H^H ^H^H ^H^H ^H^H ^H^H ^H^H ^Hscripts.^H.s^H ^Hshow_file ^H^H^H^H^H^H^H^H^Hhow_file = transform_factory_to_running^M"
);

这将输出以下内容。

test>>Management.Files.scripts.show_file = transform_factory_to_running^M

【讨论】:

  • 酷!这是一个干净的答案。非常感谢!
  • 如果反对者解释了为什么这不是一个有用的答案,我很乐意在答案中添加一些东西。
  • 您通过向后处理字符串让事情变得非常困难!请参阅我的解决方案的更新。干得好!
  • @borodin 我想避免substr 操作,而是选择在数组上工作。它有点复杂,但也更冗长。这就是我说明确的原因。
  • @simbabque:您可以使用数组或字符串——两者都可以。如果字符是退格符,我的解决方案中的算法将有一个pop @result,如果不是,则有一个push @result, $c
猜你喜欢
  • 2018-11-16
  • 1970-01-01
  • 2021-05-26
  • 1970-01-01
  • 2012-09-27
  • 2010-11-21
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
相关资源
最近更新 更多