On kaksi eri .txt tiedostoa ja pitää saada toisesta txt:stä vain saman lukujonon sisältävät rivit kuin toisen txt tiedostossa olevat. Voiko tälläisen koodin tehdä?
Eli esim txt A on tälläinen:
7968
7969
8023
8050
ja txt B:
32823978 X 4480 100
32823978 D 8023
32823978 E 6399 600
32823978 C 5760 230
32823978 E 7968 600
32823978 C 8050 230
Ja koodin ajettua txt B:stä vain saman luvun sisältävät jäljellä:
32823978 D 8023
32823978 E 7968 600
32823978 C 8050 230
Ja molemmat tiedostot sisältävät yli milj. riviä, eli ei voi ainakaan kirjottaa koodin jokaista txt A:n lukujonoa..
Jos et kerran "voi kirjoittaa tiedostoa A koodiin", niin miten olisi jos luet sen ensin vaikka taulukkoon, tai vielä parempi luet sen luvut ja merkkaat ne sanakirjaan (dictionary). Sitten vain luet tiedoston B läpi verraten löytyykö kyseistä lukua sanakirjasta.
#!/usr/bin/python3 import re luvut = dict((int(s.strip()), True) for s in open("A.txt", "r")) open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re.split("\\s+", s)[2]) in luvut)
Yllä onkin jo valmis ratkaisu, mutta tässä pari lisävinkkiä: dictin sijaan voi käyttää joukkoa (set), jolloin vältytään tarpeettomilta arvoilta. Lisäksi Pythonin regexp käännetään yllä jokaisella re.splitin suorituskerralla, mikä voi jo vähän tuntua yli miljoonassa rivissä. Vaihtoehtoisesti merkkijonon voi kääntää regexp-objektiksi re.compilella vain suorituksen alussa ja ajaa sitten splitit valmiilla regexpillä.
import re re_whitespaces = re.compile('\\s+') # ... # silmukassa sitten: ... int(re_whitespaces.split(s)[2]) ...
Testasinpa vielä huvikseni erilaisten toteutusten nopeuseroja. Dictin korvaaminen setillä ei vaikuta nopeuteen.
Versio A on Metabolixin koodi yllä.
Versio B:ssä regexp käännetään vain kerran (ja otetaan set käyttöön):
#!/usr/bin/python3 import re re_whitespaces = re.compile('\\s+') luvut = set(int(s.strip()) for s in open("A.txt", "r")) open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re_whitespaces.split(s)[2]) in luvut)
Versio C:ssä kirjoitetaan kaikki kerralla:
#!/usr/bin/python3 import re re_whitespaces = re.compile('\\s+') luvut = set(int(s.strip()) for s in open("A.txt", "r")) out = ''.join(s for s in open("B.txt", "r") if int(re_whitespaces.split(s)[2]) in luvut) open("C.txt", "w").write(out)
Versio D:ssä C:hen verrattuna jätetään int-muunnokset tekemättä:
#!/usr/bin/python3 import re re_whitespaces = re.compile('\\s+') luvut = set(s.strip() for s in open("A.txt", "r")) out = ''.join(s for s in open("B.txt", "r") if re_whitespaces.split(s)[2] in luvut) open("C.txt", "w").write(out)
Versio E:ssä D:hen verrattuna korvataan regexp-split merkkijonon split-metodilla:
#!/usr/bin/python3 luvut = set(s.strip() for s in open("A.txt", "r")) out = ''.join(s for s in open("B.txt", "r") if s.split()[2] in luvut) open("C.txt", "w").write(out)
Ja suoritusajat, kun A.txt ja B.txt sisältävät molemmat 2 miljoonaa riviä:
A: 18 s
B: 15 s
C: 14 s
D: 8,0 s
E: 3,1 s
Kaikki koodit tuottavat identtisen C.txt:n.
Muokkaus: poistettu ylimääräiset rivinvaihdot C-version outputista, ei nopeusmerkitystä
Hienoa optimointia.
Kiitos avusta!
Jos koitan tuota E vaihtoehtoa, niin ajanko vain koodin ja pidän samassa kansiossa A ja B tekstin?
Ja tarkoitus on, että B teksistä ei häviä rivien muut luvut ja numerot, vaan C txt:iin tulee kaikki rivit kokonaisuudessaan, jotka sisältävät saman lukujonon kun A:ssa on
Lisäys:
Tuo Metabolixin koodi onnistui, ongelma oli vaan että C tekstiin tuli myös rivit jossa osa A:n luvuista
Eli jos A:ssa oli:
1817226
162400
4211630
4252812
Niin C:hen tuli myös nämä:
28801151 D 17226
28801686 D 2400
28801686 D 21163
28801785 D 2812
Pitäisi saada vain ne, jossa on luku täsmälleen. Pystyykö toteuttaa?
Nyt olet kyllä itse tunaroinut jotain, koodini (ja myös mikä tahansa Chimanin versio) toimii täysin oikein. Oletko tarkistanut, ettei A.txt:ssä ole noiden rivien lukuja?
Eikun joo sori, katsoin väärää tekstiä. Kiitti tuli täysin oikein!
Osaisko joku auttaa ajan tuon:
import re luvut = dict((int(s.strip()), True) for s in open("A.txt", "r")) open("C.txt", "w").writelines(s for s in open("B.txt", "r") if int(re.split("\\s+", s)[2]) in luvut)
mutta A.txt:
57744 29258 110786 110786 112984 84169
ja B.txt:
1107562 273100 1107563 278000 1107671 270000 110786 275800
Niin tulee ValueError: invalid literal for int () with base 10 "
Mod. lisäsi kooditagit!
Sinulta puuttuu tiedostosta B.txt toinen sarake. Jos tiedoston muoto muuttuu, sinun pitää tietenkin vastaavasti muuttaa koodissa tutkittavaa saraketta (tuossa numero 2, sarakkeet alkavat nollasta).
Aihe on jo aika vanha, joten et voi enää vastata siihen.