如何从文件中随机替换文本?


9

如何将一个文本文件中的特定字符串随机替换为另一文件中的字符串?例如:

file1.txt(file has more than 200 lines):
moonwalker@address.com
hansolo@address.com
anakinskywalker@address.com
obiwankenobi@address.com
darthvader@address.com

file2.txt(file has 10-20 lines):
@adress1.com
@adress2.com
@adress3.com
@adress4.com
@adress5.com

output.txt:
moonwalker@address4.com
hansolo@address1.com
anakinskywalker@address5.com
obiwankenobi@address2.com
darthvader@address3.com

4
这不是随机的,似乎您不想重复任何事情。您是否希望它实际上是随机的,还是第二个文本文件的每一行只能使用一次?另外,它是否需要重击,还是您愿意接受其他工具?
terdon

1
@terdon好像他想要一个随机排列(所有5个元素,但按随机顺序排列)。随机排列实际上是随机的,您只需要在随机选择下一个元素时消除已经选择的元素即可。有时称为“随机排序”
thomasrutter

1
@thomasrutter是的,我知道,这就是我的回答。但这就是为什么我要求OP进行澄清,因为根据他们的需要,随机排列和随机选择都是合理的。
terdon

Answers:


9

如果您确实想要随机选择,则可以使用awk以下一种方法:

awk '
  BEGIN{FS="@"; OFS=""} 
  NR==FNR{a[NR]=$0; n++; next} 
  {$2=a[int(1 + n * rand())]; print}
' file2.txt file1.txt
moonwalker@adress2.com
hansolo@adress2.com
anakinskywalker@adress5.com
obiwankenobi@adress1.com
darthvader@adress3.com

OTOH如果您想要地址的随机排列,我建议使用类似

paste -d '' <(cut -d'@' -f1 file1.txt) <(sort -R file2.txt)
moonwalker@adress2.com
hansolo@adress1.com
anakinskywalker@adress5.com
obiwankenobi@adress4.com
darthvader@adress3.com

1
真好!我一直在考虑这样做,paste但是我没有想到cut要删除不匹配的字段。
terdon

2
粘贴解决方案的一个缺点是file1的行数多于file2的行数。代替<(sort -R file2.txt)我们可以使用类似<(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)-可能会使随机性偏向于更接近file2顶部的行。
glenn jackman

10

您可以实现此算法:

  • 将内容加载file2.txt到数组
  • 对于中的每一行file1.txt
    • 提取名称部分
    • 获取随机地址
    • 打印格式正确的输出

像这样:

mapfile -t addresses < file2.txt
while IFS='' read -r orig || [[ -n "$orig" ]]; do
    ((index = RANDOM % ${#addresses[@]}))
    name=${orig%%@*}
    echo "$name${addresses[index]}"
done < file1.txt

(特别感谢@GlennJackman和@dessert所做的改进。)


3
您可能会考虑使用mapfile -t addresses < file2.txt- 填充数组,cat这样会使您容易分词和扩展文件名。
格琳·杰克曼

2
file1.txt如果此文件没有以空行结尾(抱歉,目前无法测试),这是否捕获了最后一个非空行?如果不建议这样做while IFS='' read -r orig || [[ -n "$orig" ]]; do,请参阅逐行读取文件,将值分配给变量·SO
甜点,

2
@janos刚刚找到了一个关于这个主题的很好的问题:Shell脚本读到了最后一行缺少的内容
甜点

5

您可以使用shuf(可能需要sudo apt install shuf)来随机播放第二个文件的行,然后使用它们来替换:

$ awk -F'@' 'NR==FNR{a[NR]=$1;next}{print a[FNR]"@"$2} ' file1 <(shuf file2)
moonwalker@adress3.com
hansolo@adress1.com
anakinskywalker@adress5.com
obiwankenobi@adress4.com
darthvader@adress2.com

shuf只需将其输入线的顺序随机化即可。awk那里的命令将首先读取所有file1(NR==FNR仅在读取第一个文件时为true),然后将第二个字段(字段由定义@,因此这是域)保存在关联数组中a,该数组的值是domain和其键是行号。然后,当我们转到下一个文件时,它将仅打印a该行号存储的内容以及文件2中相同行号的内容。

请注意,这假设两个文件的行数完全相同,并且实际上不是“随机”的,因为这将不允许重复任何操作。但这看起来像您想要的。


5

Python 2.7和3解决方案

该解决方案用每次从替换字符串列表的行集中随机选择的字符串替换输入文件每一行中第一次出现的任意给定字符串(“ needle”)。

#!/usr/bin/python
from __future__ import print_function
import sys, random

needle = sys.argv[1]

if sys.argv[2] == '-':
    f_replacements = sys.stdin
else:
    f_replacements = open(sys.argv[2])
with f_replacements:
    replacements = [l.rstrip('\n') for l in f_replacements]
if not replacements:
    raise ValueError('No replacement strings given')

if len(sys.argv) <= 3 or sys.argv[3] == '-':
    f_in = sys.stdin
else:
    f_in = open(sys.argv[3])
with f_in:
    for s in f_in:
        rep = replacements[random.randrange(len(replacements))]
        print(s.rstrip('\n').replace(needle, rep, 1))

将针锚定在字符串的开头或结尾或完全使用正则表达式应该几乎是微不足道的。

用法

python replace-random.py NEEDLE REPLACEMENTS-FILE [INPUT-FILE]

例:

python replace-random.py '@address.com' file2.txt file1.txt

要么

python replace-random.py '@address.com' file2.txt < file1.txt

3

这是一种perl方式:

#!/usr/bin/perl
use warnings;
use strict;
use Tie::File;

tie my @file1,'Tie::File','file1.txt' or die "Can't open file1.txt\n";
tie my @file2,'Tie::File','file2.txt' or die "Can't open file2.txt\n";

for my $file_index (0..$#file1) {
   my $suffix = $file2[int(rand($#file2+1))];
   $file1[$file_index] =~ s/@.*$/$suffix/;
}

untie @file1;
untie @file2;

2

另一个bash解决方案。它使用bash内置的字符串替换功能。它还假定file2.txt仅包含替换字符串。如果没有,可以先使用grep -o <replace> file2.txt

shuf

#search string
Search="@address.com"
for lines in $(grep $Search file1.txt)
do 
    echo ${lines/$Search/$(shuf file2.txt -n 1)} 
done

没有shuf(几乎是纯净的bash

在这里,我们必须首先创建一个shuf类似这样的函数

bshuf () 
{ 
    nlines=$(( $(wc -l < $1) + 1))
    rand=0
    while [ "$rand" -eq 0 ]; do
        rand=$(( $RANDOM % nlines ))
    done
    echo $(head -n $rand $1 | tail -1)
}

那就差不多了

for lines in $(grep $Search file1.txt) 
do 
    echo ${lines/$Search/$(bshuf file2.txt)}
done

测试:

$ for lines in $(grep $Search file1.txt); do echo ${lines/$Search/$(bshuf file2.txt)} ; done
moonwalker@adress4.com
hansolo@adress2.com
anakinskywalker@adress2.com
obiwankenobi@adress3.com
darthvader@adress5.com
$ 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.