【问题标题】:Perl Inserting a string from a file after every occurence of a slash in a urlPerl 在 url 中每次出现斜杠后从文件中插入一个字符串
【发布时间】:2013-02-26 12:39:27
【问题描述】:

我有以下网址:

FILE1.txt

http://www.stackoveflow.com/dog/cat/rabbit/hamster/
192.168.192.168/lion/tiger/elephant/

FILE2.txt

HELLO
GOODBYE

我想要达到的输出:

http://www.stackoveflow.com/dogHELLO/cat/rabbit/hamster/
http://www.stackoveflow.com/dog/catHELLO/rabbit/hamster/
http://www.stackoveflow.com/dog/cat/rabbitHELLO/hamster/
http://www.stackoveflow.com/dog/cat/rabbit/hamsterHELLO/
http://www.stackoveflow.com/dog/cat/rabbit/hamster/HELLO

http://www.stackoveflow.com/dogGOODBYE/cat/rabbit/hamster/
http://www.stackoveflow.com/dog/catGOODBYE/rabbit/hamster/
http://www.stackoveflow.com/dog/cat/rabbitGOODBYE/hamster/
http://www.stackoveflow.com/dog/cat/rabbit/hamsterGOODBYE/
http://www.stackoveflow.com/dog/cat/rabbit/hamster/GOODBYE

192.168.192.168/lionHELLO/tiger/elephant/
192.168.192.168/lion/tigerHELLO/elephant/
192.168.192.168/lion/tiger/elephantHELLO/
192.168.192.168/lion/tiger/elephant/HELLO

192.168.192.168/lionGOODBYE/tiger/elephant/
192.168.192.168/lion/tigerGOODBYE/elephant/
192.168.192.168/lion/tiger/elephantGOODBYE/
192.168.192.168/lion/tiger/elephant/GOODBYE

如您所见,字符串 HELLOGOODBYE 被插入到每个斜杠之后,如果斜杠之后已经有一个字符串,它将在之后附加 HELLOGOODBYE(例如 http://www.stackoveflow.com/dogHELLO/cat/rabbit/hamster/等等)。

我的尝试

use strict;
use warnings;

my @f1 = do {
   open my $fh, '<', 'FILE1.txt';
   <$fh>;
};
chomp @f1;

my @f2 = do {
  open my $fh, '<', 'FILE2.txt';
  <$fh>;
};
chomp @f2;

for my $f1 (@f1) {
  my @fields = $f1 =~ m{[^/]+}g;
  for my $f2 (@f2) {
    for my $i (0 .. $#fields) {
      my @new = @fields;
      $new[$i] .= $f2;
      print qq{/$_/\n}, for join '/', @new;
    }
    print "\n\n";
  }
}
#courtesy of Borodin

但是此代码不适合在http:// 部分中带有斜杠的网址,因为它们在不应该的情况下被http:HELLO/ 替换。

如果没有字符串,它也不会在斜杠之后放置HELLOGOODBYE,例如http://www.stackoveflow.com/dog/cat/rabbit/hamster/&lt;--SHOULD PUT HELLO AFTER THIS SLASH AS WELL BUT DOSN'T

似乎此代码删除了斜杠,然后重新插入了 FILE2.txt 中的字符串,而不是在正确的开始位置插入 HELLOGOODBYE

我的问题

是否有更好的方法来实现我需要的输出,或者我可以对现有代码做些什么来解决上述问题?

非常感谢您的帮助,非常感谢

【问题讨论】:

  • 你能展示任何你尝试过的方法吗?
  • FILE1.txt文件路径中的字符串,还是恰好使用斜杠作为分隔符的字符串?
  • @Wooble - 更好地解释和描述了我的问题,希望足以重新打开?谢谢
  • 所以它们不是文件路径,现在它们是?在最后的斜线之后你什么都不想要,但现在你想要了吗?现在整个字符串的开头有http:/,您想保持原样吗?您仍然没有尝试自己解决这个问题,只是复制了我的解决方案(减去基本的use autodie)并要求进一步增强。 这不是礼貌行为,当然也不属于 Stack Overflow。自己努力,遇到特定问题时再回来。
  • @Borodin - 我能解释一下吗,我明白你来自哪里。为了解释我的问题,我的原始问题被高度简化,这就是为什么我最初没有包括 http:/ 和最后的斜杠部分,因为我认为通过了解这一点,我将能够更改我的代码以适应 http :/ 和我自己的最后一个斜线。但是,您的代码和执行方式比我尝试执行的方式要好得多,因此决定改用您的方法,这就是我现在陷入尝试进行 http:/ 和最终斜杠更改的地方,请参阅下一条评论

标签: string perl permutation


【解决方案1】:

这是散文中的算法:

Open File2.txt. Read in all lines, removing the newline. We call the array @words.

Open File2.txt. We call the file handle $fh.

As long as we can read a $line from $fh:

    Remove the newline, remove starting and ending slashes.
    Split the $line at every slash, call the array @animals.

    Loop through the @words, calling each element $word:

        Loop through the indices of the @animals, calling each index $i:

            Make a @copy of the @animals.
            Append the $word to the $i-th element of @copy.
            Join the @copy with slashes, surround it with slashes, and print with newline.

        Print an empty line.

【讨论】:

    【解决方案2】:

    这个程序会按照你的要求做。

    use strict;
    use warnings;
    use autodie;
    
    my @f1 = do {
      open my $fh, '<', 'FILE1.txt';
      <$fh>;
    };
    chomp @f1;
    
    my @f2 = do {
      open my $fh, '<', 'FILE2.txt';
      <$fh>;
    };
    chomp @f2;
    
    for my $f1 (@f1) {
      my @fields = $f1 =~ m{[^/]+}g;
      for my $f2 (@f2) {
        for my $i (0 .. $#fields) {
          my @new = @fields;
          $new[$i] .= $f2;
          print qq{/$_/\n}, for join '/', @new;
        }
        print "\n\n";
      }
    }
    

    输出

    /dogHELLO/cat/rabbit/hamster/
    /dog/catHELLO/rabbit/hamster/
    /dog/cat/rabbitHELLO/hamster/
    /dog/cat/rabbit/hamsterHELLO/
    
    
    /dogGOODBYE/cat/rabbit/hamster/
    /dog/catGOODBYE/rabbit/hamster/
    /dog/cat/rabbitGOODBYE/hamster/
    /dog/cat/rabbit/hamsterGOODBYE/
    
    
    /lionHELLO/tiger/elephant/
    /lion/tigerHELLO/elephant/
    /lion/tiger/elephantHELLO/
    
    
    /lionGOODBYE/tiger/elephant/
    /lion/tigerGOODBYE/elephant/
    /lion/tiger/elephantGOODBYE/
    

    【讨论】:

    • 太好了,非常感谢,这正是我想要实现的,谢谢。如果这些现在确实是文件路径或 URL,例如 http://stackoverflow.com/dog/cat/rabbit/hamster/ - 我将如何让它不关心 http:// 部分中的斜线?
    • 请查看我已编辑的问题,在关闭时必须对其进行编辑,感谢您的帮助
    • 我真的坚持尝试容纳 http:// 并在最后的斜线之后添加它,任何进一步的帮助将不胜感激,非常感谢
    【解决方案3】:

    您可以使用正则表达式来完成所有操作,而不是在每个斜杠上分割线。

    更新版本:

    #!usr/bin/perl
    use strict;
    use warnings;
    
    my @insert_words = qw/HELLO GOODBYE/;
    my $word = 0;
    
    while (<DATA>)
    {
        chomp;
        foreach my $word (@insert_words)
        {
            my $repeat = 1;
            while ((my $match=$_) =~ s|(?<!/)(?:/(?!/)[^/]*){$repeat}[^/]*\K|$word|)
            {
                print "$match\n";
                $repeat++;
            }
            print "\n";
        }
    }
    
    __DATA__
    /dog/cat/rabbit/hamster/
    http://www.stackoverflow.com/dog/cat/rabbit/hamster/
    

    关键是替换运算符:s|(?&lt;!/)(?:/(?!/)[^/]*){$repeat}[^/]*\K|$word|

    (?&lt;!/)(?!/) 分别是负后瞻和前瞻。他们确保我们只匹配一个/,从而忽略http://

    (?:/(?!/)[^/]*){$repeat} 是一个必须匹配指定次数的捕获组,我们增加该次数直到不再匹配。

    我不得不使用[^/]* 而不是[^/]+ 来满足您在字符串末尾匹配的要求。这就是为什么需要后视和前瞻的原因。

    \K 的意思是“匹配到目前为止的所有内容,但不要将其包含在匹配本身中。”因此,我们不必担心在替换中包含匹配字符串的整个开头。

    注意:r 选项是另一种在不修改原始字符串的情况下执行替换的方法。但是,它需要 Perl 5.16(感谢 Amon)。因此我将其从示例中删除。

    【讨论】:

    • @dan1111 /r 选项是相当新的(我认为是 v16)。这可能会导致一些问题。传统成语是(my $copy = $original) =~ s/foo//
    • @amon,我最近遇到了这个选项,但没有意识到它是新的。感谢您提供信息。
    • @dan1111 - 这是我在尝试运行它时遇到的错误。我在两个不同的系统上遇到同样的错误。 Bareword found where operator expected at stringTest.pl line 13, near "s|(?:/\w+){$repeat}\w+\K|$word|r" syntax error at stringTest.pl line 13, near "s|(?:/\w+){$repeat}\w+\K|$word|r" Global symbol "$match" requires explicit package name at stringTest.pl line 15. syntax error at stringTest.pl line 20, near "}" Execution of stringTest.pl aborted due to compilation errors.,谢谢
    • @perl-user,问题在于\w+ 只匹配单词字符:字母、数字和下划线。您可以在模式中出现\w+ 的每个位置使用[^/]+。这将匹配除斜线之外的任何内容。
    • @perl-user,您可以将模式中的每个/ 替换为您要匹配的字符的字符类。例如,如果您想匹配/,=,而不是/。同样,否定类必须更新为[^/,=]。但是,您需要仔细考虑要匹配的内容。 url 包含非字母数字字符,例如 :.。如果你想匹配那些它会弄乱 URL 部分的排除。
    猜你喜欢
    • 2020-05-04
    • 2022-01-25
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多