Làm thế nào để thay thế văn bản ngẫu nhiên từ tập tin?


9

Làm cách nào tôi có thể thay thế ngẫu nhiên các chuỗi cụ thể trong một tệp văn bản bằng các chuỗi từ một tệp khác? Ví dụ:

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
Điều đó không ngẫu nhiên, có vẻ như bạn không muốn bất cứ điều gì lặp lại. Bạn có muốn nó thực sự ngẫu nhiên, hay mỗi dòng của tệp văn bản thứ hai chỉ được sử dụng một lần? Ngoài ra, nó cần phải được bash, hoặc bạn đang mở cho các công cụ khác?
terdon

1
@terdon Có vẻ như anh ấy muốn một hoán vị ngẫu nhiên (tất cả 5 yếu tố nhưng theo thứ tự ngẫu nhiên). Một hoán vị ngẫu nhiên thực sự là ngẫu nhiên, bạn chỉ cần loại bỏ các yếu tố đã được chọn khi chọn ngẫu nhiên các yếu tố tiếp theo. Đôi khi được gọi là "sắp xếp ngẫu nhiên"
thomasrutter

1
@thomasrutter vâng, tôi biết điều đó và đó là câu trả lời của tôi. Nhưng đó là lý do tại sao tôi yêu cầu OP làm rõ vì cả hoán vị ngẫu nhiên và chọn ngẫu nhiên sẽ hợp lý tùy thuộc vào những gì họ cần.
terdon

Câu trả lời:


9

Nếu bạn thực sự muốn một lựa chọn ngẫu nhiên, thì đây là một cách sử dụng 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 nếu bạn muốn hoán vị ngẫu nhiên các địa chỉ, tôi sẽ đề xuất một cái gì đó như

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
Đẹp! Tôi đang xem xét thực hiện pastenhưng tôi không sử dụng cutđể loại bỏ trường không khớp.
terdon

2
Một nhược điểm của giải pháp dán là khi file1 có nhiều dòng hơn file2. Thay vì <(sort -R file2.txt)chúng ta có thể sử dụng một cái gì đó như <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- điều đó có thể làm lệch tính ngẫu nhiên có lợi cho các dòng gần với đỉnh của tệp2.
glenn jackman

10

Bạn có thể thực hiện thuật toán này:

  • Tải nội dung của file2.txtmột mảng
  • Đối với mỗi dòng trong file1.txt:
    • Trích xuất phần tên
    • Nhận một địa chỉ ngẫu nhiên
    • In đầu ra được định dạng chính xác

Như thế này:

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

(Cảm ơn đặc biệt đến @GlennJackman và @d PLAY vì những cải tiến.)


3
Bạn có thể xem xét việc điền vào mảng với mapfile -t addresses < file2.txt- sử dụng catnhư các đối tượng mà bạn chia tách từ và mở rộng tên tệp.
glenn jackman

2
Điều này có bắt được dòng không trống cuối cùng không file1.txtnếu tệp này không kết thúc bằng một dòng trống (xin lỗi, không thể kiểm tra tại thời điểm này)? Nếu không tôi khuyên bạn while IFS='' read -r orig || [[ -n "$orig" ]]; do, hãy xem Đọc một dòng tệp bằng cách gán giá trị cho một biến · SO .
tráng miệng

2
@janos Chỉ cần tìm một câu hỏi rất hay về chủ đề: Kịch bản Shell đọc thiếu dòng cuối cùng
món tráng miệng

5

Bạn có thể sử dụng shuf(bạn có thể cần sudo apt install shuf) để xáo trộn các dòng của tệp thứ hai và sau đó sử dụng chúng để thay thế:

$ 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

shufchỉ đơn giản là ngẫu nhiên thứ tự của các dòng đầu vào của nó. Các awklệnh có đầu tiên sẽ đọc tất cả các file1 ( NR==FNRsẽ chỉ có đúng trong khi tập đầu tiên đang được đọc), và tiết kiệm trường thứ hai (trường được định nghĩa bởi @, vì vậy đây là tên miền) trong mảng kết hợp acó giá trị là các lĩnh vực và có khóa là số dòng. Sau đó, khi chúng ta đến tệp tiếp theo, nó sẽ chỉ in bất cứ thứ gì được lưu trữ acho số dòng này, cùng với những gì trong tệp 2 cho cùng một số dòng.

Lưu ý rằng điều này giả sử cả hai tệp có cùng số dòng và không thực sự là "ngẫu nhiên", vì nó sẽ không cho phép bất cứ điều gì được lặp lại. Nhưng đó trông giống như những gì bạn muốn yêu cầu.


5

Giải pháp Python 2.7 và 3

Giải pháp này thay thế lần xuất hiện đầu tiên của một chuỗi đã cho tùy ý (chuỗi kim kim) trong mỗi dòng của tệp đầu vào bằng một chuỗi mỗi lần được chọn ngẫu nhiên từ bộ dòng của danh sách chuỗi thay thế.

#!/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))

Việc neo kim vào đầu hoặc cuối chuỗi hoặc sử dụng các biểu thức chính quy hoàn toàn là điều gần như không đáng kể.

Sử dụng

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

Thí dụ:

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

hoặc là

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

3

Đây là một cách hay:

#!/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

Một giải pháp bash khác. Nó sử dụng tính năng thay thế chuỗi tích hợp bash. Nó cũng giả sử chỉ file2.txtchứa các chuỗi thay thế. Nếu không, chúng có thể được lọc đầu tiên bằng cách sử dụnggrep -o <replace> file2.txt

Với shuf

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

Không có shuf(gần như nguyên chất bash)

Ở đây chúng ta phải tạo một hàm đầu tiên bắt chước shufnhư vậy

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

Sau đó, nó là tương tự

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

Kiểm tra:

$ 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
$ 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.