Como substituir o texto aleatoriamente do arquivo?


9

Como posso substituir aleatoriamente cadeias específicas em um arquivo de texto por cadeias de outro arquivo? Por exemplo:

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
Isso não é aleatório, parece que você não quer nada repetido. Deseja que seja realmente aleatório ou cada linha do segundo arquivo de texto deve ser usada apenas uma vez? Além disso, ele precisa ser bash ou você está aberto a outras ferramentas?
terdon

11
@terdon Parece que ele quer uma permutação aleatória (todos os 5 elementos, mas em uma ordem aleatória). Uma permutação aleatória é realmente aleatória, basta eliminar os elementos já escolhidos ao selecionar aleatoriamente o próximo elemento. Às vezes chamado de "classificação aleatória"
thomasrutter

11
@thomasrutter sim, eu sei disso e é isso que minha resposta faz. Mas é por isso que pedi ao OP que esclareça, já que uma permutação aleatória e uma escolha aleatória seriam razoáveis, dependendo do que elas precisam.
terdon

Respostas:


9

Se você realmente deseja uma seleção aleatória, aqui está uma maneira de usar 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, se você quiser uma permutação aleatória dos endereços, sugiro algo como

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

11
Agradável! Eu estava pensando em fazê-lo, pastemas não me ocorreu usar cutpara remover o campo não correspondente.
terdon 17/17/17

2
Uma desvantagem da solução de colagem é quando o arquivo1 possui mais linhas que o arquivo2. Em vez de <(sort -R file2.txt)usarmos algo como <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- que pode distorcer a aleatoriedade em favor de linhas mais próximas do topo do arquivo2.
Glenn Jackman

10

Você pode implementar este algoritmo:

  • Carregar o conteúdo de file2.txtem uma matriz
  • Para cada linha em file1.txt:
    • Extrair a parte do nome
    • Obter um endereço aleatório
    • Imprimir a saída formatada corretamente

Como isso:

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

(Agradecimentos especiais a @GlennJackman e @dessert pelas melhorias.)


3
Você pode considerar preencher a matriz com mapfile -t addresses < file2.txt- usando catdessa forma sujeita você à divisão de palavras e à expansão do nome do arquivo.
Glenn Jackman

2
Isso captura a última linha não vazia de file1.txtse esse arquivo não terminar com uma linha vazia (desculpe, não é possível testar no momento)? Caso contrário, recomendo while IFS='' read -r orig || [[ -n "$orig" ]]; do, consulte Ler um arquivo linha por linha atribuindo o valor a uma variável · SO .
dessert

2
@janos Acabei de encontrar uma pergunta muito boa sobre o tópico: Script do shell lido ausente na última linha
sobremesa

5

Você pode usar shuf(talvez seja necessário sudo apt install shuf) para embaralhar as linhas do segundo arquivo e usá-las para substituir:

$ 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

shufsimplesmente randomiza a ordem de suas linhas de entrada. O awkcomando lá primeiro lerá todo o arquivo1 ( NR==FNRserá verdadeiro somente enquanto o primeiro arquivo estiver sendo lido) e salva o segundo campo (os campos são definidos por @, portanto esse é o domínio) no array associativo acujos valores são os domínios e cujas teclas são os números de linha. Então, quando chegarmos ao próximo arquivo, ele simplesmente imprimirá o que foi armazenado apara esse número de linha, juntamente com o que está no arquivo 2 para o mesmo número de linha.

Observe que isso pressupõe que os dois arquivos têm exatamente o mesmo número de linhas e não está sendo "aleatório", pois não permitirá que nada seja repetido. Mas isso parece com o que você queria pedir.


5

Solução Python 2.7 e 3

Esta solução substitui a primeira ocorrência de uma única sequência arbitrária especificada (a “agulha”) em todas as linhas do arquivo de entrada por uma sequência sempre escolhida aleatoriamente no conjunto de linhas da lista de sequências de substituições.

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

Deve ser quase trivial ancorar a agulha no início ou no final da corda ou usar expressões regulares por completo.

Uso

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

Exemplo:

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

ou

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

3

Aqui está uma maneira 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

Outra solução do bash. Ele usa o recurso de substituição de string incorporado do bash. Ele também assume que file2.txtcontém apenas as seqüências de substituição. Caso contrário, eles podem ser filtrados primeiro usandogrep -o <replace> file2.txt

Com shuf

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

Sem shuf(quase puro bash)

Aqui temos que criar primeiro uma função que imita shufassim

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

Então é semelhante

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

Teste:

$ 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
$ 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.