Dosyadan rastgele metin nasıl değiştirilir?


9

Bir metin dosyasındaki belirli dizeleri başka bir dosyadaki dizelerle rastgele nasıl değiştirebilirim? Örneğin:

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
Bu rastgele değil, tekrarlanan bir şey istemiyormuşsunuz gibi görünüyor. Gerçekten rastgele olmasını mı istiyorsunuz, yoksa ikinci metin dosyasının her satırı yalnızca bir kez mi kullanılmalıdır? Ayrıca bash olması mı gerekiyor, yoksa başka araçlara açık mısınız?
terdon

1
@terdon Görünüşe göre rastgele bir permütasyon istiyor (5 elementin hepsi rastgele bir sırayla). Rastgele bir permütasyon aslında rastgeledir, sadece bir sonraki elemanı rastgele seçerken önceden seçilmiş elemanları ortadan kaldırmanız gerekir. Bazen "rastgele sıralama" olarak adlandırılır
thomasrutter

1
@homasrutter evet, bunu biliyorum ve cevabım bunu yapıyor. Ama bu yüzden OP'den açıklık getirmesini istedim, çünkü hem rastgele bir permütasyon hem de rastgele bir seçim ihtiyaç duyduklarına bağlı olarak makul olurdu.
terdon

Yanıtlar:


9

Eğer varsa gerçekten rastgele bir seçim istiyorum, sonra buraya kullanarak bir yolu 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 adreslerin rastgele bir permütasyonunu istiyorsanız, şöyle bir şey öneririm

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
Güzel! Bunu yapmaya bakıyordum pasteama cuteşleşmeyen alanı kaldırmak için kullanmak bana gelmedi .
terdon

2
Yapıştır çözümünün bir dezavantajı, dosya1'in dosya2'den daha fazla satırı olması. Bunun yerine rastgele bir dosyayı <(sort -R file2.txt)kullanabiliriz <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)ki bu, rasgele dosyayı eğe2'nin üst kısmına yakın olan çizgiler lehine çevirebilir.
glenn jackman

10

Bu algoritmayı uygulayabilirsiniz:

  • İçeriğini file2.txtdiziye yükle
  • İçindeki her satır için file1.txt:
    • İsim bölümünü çıkarın
    • Rastgele bir adres alın
    • Çıktıyı doğru biçimlendirilmiş olarak yazdırın

Bunun gibi:

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 ve @ tatlı için iyileştirmeler için özel teşekkürler.)


3
Diziyi, sözcük bölme ve dosya adı genişletme işlemlerine maruz bıraktığınız şekilde mapfile -t addresses < file2.txtkullanarak düşünebilirsiniz cat.
glenn jackman

2
file1.txtBu dosya boş bir satırla bitmezse son boş satırını yakalar mı? (Üzgünüm, şu anda test yapamıyorum)? Tavsiye etmiyorsam while IFS='' read -r orig || [[ -n "$orig" ]]; do, bkz . Bir değişkeni · SO değerine atayarak bir dosyayı satır satır okuma .
tatlı

2
@janos Konu hakkında çok iyi bir soru buldum: Shell script read last line eksik
tatlı

5

Sen kullanabilirsiniz shuf(eğer gerekebilir sudo apt install shufikinci dosyanın satırları karıştırmak ve sonra yerine bunları kullanmak için):

$ 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

shufsadece girdi satırlarının sırasını rastgele seçer. Buradaki awkkomut ilk olarak tüm dosya1'i okuyacaktır ( NR==FNRyalnızca ilk dosya okunurken doğru olacaktır) ve değerleri, etki alanları olan @ilişkilendirilebilir dizide ikinci alanı (alanlar tarafından tanımlanır , bu nedenle etki alanıdır) kaydeder ave tuşları satır numaralarıdır. Ardından, bir sonraki dosyaya geldiğimizde, abu satır numarası için depolanan her şeyi ve aynı satır numarası için dosya 2'de bulunanları yazdıracaktır .

Bunun, her iki dosyanın da aynı sayıda satıra sahip olduğunu ve hiçbir şeyin tekrarlanmasına izin vermeyeceği için aslında "rastgele" olmadığını varsaydığını unutmayın. Ama bu, istemek istediğin gibi görünüyor.


5

Python 2.7 ve 3 çözümü

Bu çözüm, girdi dosyasının her satırında rasgele verilen tek bir dizenin (“iğne”) ilk oluşumunu, her seferinde değiştirme dizesi listesinin satır kümesinden rastgele seçilen bir dizeyle değiştirir.

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

İğneyi ipin başına veya sonuna bağlamak veya düzenli ifadeleri tamamen kullanmak neredeyse önemsiz olmalıdır.

kullanım

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

Misal:

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

veya

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

3

İşte bir perl yolu:

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

Başka bir bash çözümü. Bash yerleşik dize değiştirme özelliğini kullanır. Ayrıca file2.txtyalnızca değiştirme dizelerini içerdiğini varsayar . Değilse ilk önce filtrelenebilirlergrep -o <replace> file2.txt

İle shuf

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

Olmadan shuf(neredeyse saf bash)

Burada taklit ilk bir işlev oluşturmak zorunda shufböylece gibi

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

Sonra benzer

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

Ölçek:

$ 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
$ 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.