[Risolto]Cerca e trasforma data in file .txt
- UbuNuovo
- Imperturbabile Insigne
- Messaggi: 4446
- Iscrizione: sabato 12 dicembre 2009, 20:58
- Desktop: Mate
- Distribuzione: Ubuntu Mate 22.04.1 LTS
- Sesso: Maschile
- Contatti:
Re: Cerca e trasforma data in file .txt
Sì, meglio usare le funzioni di AWK https://www.gnu.org/software/gawk/manua ... tions.html
- vaeVictis
- Imperturbabile Insigne
- Messaggi: 4703
- Iscrizione: venerdì 27 luglio 2012, 17:58
- Desktop: Gnome
- Distribuzione: Ubuntu 20.04 64bit
Re: Cerca e trasforma data in file .txt
Ho finito lo script con awk.
Domani lo posto.
Per ora ti lascio il benchmark sullo stesso file su cui ho testato il tuo e il mio script precedente:
Codice: Seleziona tutto
$ time awk -f mioScriptAwk.awk dati.txt >> $(mktemp -p .)
real 0m0,217s
user 0m0,184s
sys 0m0,032s
«I fear not the man who has practiced 10000 kicks once,
but I fear the man who has practiced one kick 10000 times.»
- vaeVictis
- Imperturbabile Insigne
- Messaggi: 4703
- Iscrizione: venerdì 27 luglio 2012, 17:58
- Desktop: Gnome
- Distribuzione: Ubuntu 20.04 64bit
Re: Cerca e trasforma data in file .txt
Ho rifatto alcuni controlli facendo riferimento al file di dati creato con lo script di UbuNuovo, con intervallo di date da 00-01-01 a 21-06-26 (quindi un file con 7848 date su altrettante righe).
Per quanto riguarda lo script di UbuNuovo, si ha un tempo di esecuzione:
Codice: Seleziona tutto
$ time ./ubuNuovo.sh
real 0m33,963s
user 0m25,394s
sys 0m10,228s
Per quanto riguarda il mio precedente script che usa sed:
Codice: Seleziona tutto
vaevictis@vaevictis-N56VV:~/Programmazione/Bash/ConversioneDate$ time ./mioScriptSed.sh
real 0m16,370s
user 0m14,337s
sys 0m3,559s
La criticità è che fa più chiamate a sed e non mi piace.
Infine, si può usare gawk.
Crea un file nomeScriptAwk.awk contenente:
Codice: Seleziona tutto
{
while (match($0, /([0-9]{2})-([0-9]{2})-([0-9]{2})/, arr) )
{
date = arr[0]
arr[1] = (arr[1] < 70 ? 20 : 19) arr[1]
time = sprintf("%s %s %s 1 0 0", arr[1], arr[2], arr[3])
secs = mktime(time)
new_date = strftime("%A, %d %B %Y", secs)
gsub(date, new_date)
}
print
}
1)
Codice: Seleziona tutto
gawk -f nomeScriptAwk.awk pathDelFileDaModificare >> $(mktemp -p .)
Il file di output ti viene creato in un file tmp contenuto nella directory dove lanci lo script (per modificare questo comportamento ti basta lavorare sulla redirezione dell'output nel comando qui sopra)
Il file da modificare non viene toccato.
2)
Codice: Seleziona tutto
gawk -i inplace -f nomeScriptAwk.awk pathDelFileDaModificare
Il file da modificare viene modificato in place, quindi perdi il file originale.
3)
Codice: Seleziona tutto
gawk -i inplace -v INPLACE_SUFFIX=.backup -f nomeScriptAwk.awk pathDelFileDaModificare
Il file da modificare viene modificato in place, ma ti viene creato un file di backup con lo stesso nome del file originale, più il suffisso backup.
Per quanto riguarda le performance, c'è una significativa differenza. Si passa dalle decine di secondi alla frazione di secondo!
L'opzione più lenta è la terza, per la quale:
Codice: Seleziona tutto
$ time gawk -i inplace -v INPLACE_SUFFIX=.backup -f mioScriptAwk.awk dati.txt
real 0m0,260s
user 0m0,236s
sys 0m0,024s
È lo stesso comportamento del comando date, che come soglia ha 69 invece di 70.
Quindi non credo sia un problema.
Qualora lo dovesse essere, si risolve facilmente, basta che fai sapere.
Con questo passo e chiudo.
Fai sapere, ciao.
Edit:
Questo script ha una criticità spiegata alla fine di questo messaggio
«I fear not the man who has practiced 10000 kicks once,
but I fear the man who has practiced one kick 10000 times.»
-
- Imperturbabile Insigne
- Messaggi: 2848
- Iscrizione: domenica 11 maggio 2008, 18:03
- Desktop: plasma
- Distribuzione: 22.04
- Località: Palermo
Re: Cerca e trasforma data in file .txt
momento momento momento!Con questo passo e chiudo.
@vaeVictis potresti prima spiegare un po' lo script, specialmente queste righe
Codice: Seleziona tutto
arr[1] = (arr[1] < 70 ? 20 : 19) arr[1]
time = sprintf("%s %s %s 1 0 0", arr[1], arr[2], arr[3])
- vaeVictis
- Imperturbabile Insigne
- Messaggi: 4703
- Iscrizione: venerdì 27 luglio 2012, 17:58
- Desktop: Gnome
- Distribuzione: Ubuntu 20.04 64bit
Re: Cerca e trasforma data in file .txt
@rai@vaeVictis potresti prima spiegare un po' lo script, specialmente queste righeCodice: Seleziona tutto
arr[1] = (arr[1] < 70 ? 20 : 19) arr[1] time = sprintf("%s %s %s 1 0 0", arr[1], arr[2], arr[3])
Certo.
Allora, per ogni riga letta lo script fa la seguente cosa:
1) ciclo while che si ripete finché trova in $0 un match con l'espressione regolare ([0-9]{2})-([0-9]{2})-([0-9]{2}), che viene eventualmente inserita nella variabile arr
Codice: Seleziona tutto
while (match($0, /([0-9]{2})-([0-9]{2})-([0-9]{2})/, arr) )
Se viene trovato il match, awk schiaffa tutto il match in arr[0] e siccome ho usato i gruppi schiaffa la prima parte in arr[1], la seconda in arr[2] e la terza in arr[3].
Quindi se sulla riga ci fosse la data 20-02-12, avrei:
Codice: Seleziona tutto
arr[0] = 20-02-12
arr[1] = 20
arr[1] = 02
arr[1] = 12
Codice: Seleziona tutto
date = arr[0]
3) uso l'operatore ternario A ? B : C, che vuol dire: "se A è verificata, allora esegui B, altrimenti C.
Codice: Seleziona tutto
arr[1] = (arr[1] < 70 ? 20 : 19) arr[1]
E poi "appende" il valore arr[1] a quanto scritto.
Quindi se ho arr[1] pari a 30, diventerà 2030.
E poi sovrascrivo il valore di arr[1] (a sinistra del'uguale) con quello che ho ottenuto a destra dell'uguale.
Per capirci, è come se avessi scritto:
Codice: Seleziona tutto
if (arr[1] < 70)
arr[1] = "20" + arr[1]
else
arr[1] = "19" + arr[1]
4) Formatto la stringa con i valori che ho nelle varie parti di arr e schiaffo il suo risultato in time.
Codice: Seleziona tutto
time = sprintf("%s %s %s 1 0 0", arr[1], arr[2], arr[3])
Codice: Seleziona tutto
2020 02 12 1 0 0
5) Ho formattato la stringa time in quel modo perché mi serve come argomento di mktime per calcolare i secondi dalla EPOCH.
Codice: Seleziona tutto
secs = mktime(time)
6) Una volta che ho i secondi corrispondenti alla prima ora di quel particolare giorno, posso risalire alla new_date, usando la funzione strftime
Codice: Seleziona tutto
new_date = strftime("%A, %d %B %Y", secs)
In pratica, fa la stessa cosa che fa date con questo comando:
Codice: Seleziona tutto
$ date --date="20-02-12" "+%A, %d %B %Y"
mercoledì, 12 febbraio 2020
7) Quindi
Codice: Seleziona tutto
gsub(date, new_date)
Quindi nel nostro esempio:
Codice: Seleziona tutto
20-02-12
Codice: Seleziona tutto
mercoledì, 12 febbraio 2020
Quindi se trova un'altra data (diversa dalla prima perché ovviamente tutte quelle uguali sono state già modificate dal comando precedente), ripete la procedura descritta.
Altrimenti esce dal ciclo while e passa al punto successivo.
8 )
Codice: Seleziona tutto
print
Questa $0 sarà quella con le modifiche effettuate, se ovviamente ha trovato date da modificare, o semplicemente quella letta, se già nel primo ciclo while non avesse dovuto trovare nulla.
Stampata la riga, legge la successiva e ricomincia tutto da capo.
p.s.: per lo script, sono stato aiutato da un utente di Stackoverflow. Io avrei continuato a provare la command substitution come da precedente messaggio, che non è però possibile... E neanche si chiama command substitution come mi ha fatto notare un altro utente samurai cintura buco nero quindicesimo dan di awk con sbidiguda prematurata
p.p.s.: @steff credo che ci siano le condizioni per bollare come risolta questa discussione e permettermi ora di dormire tranquillo
Scherzi a parte, fammi sapere. Ciao
«I fear not the man who has practiced 10000 kicks once,
but I fear the man who has practiced one kick 10000 times.»
-
- Imperturbabile Insigne
- Messaggi: 2848
- Iscrizione: domenica 11 maggio 2008, 18:03
- Desktop: plasma
- Distribuzione: 22.04
- Località: Palermo
Re: Cerca e trasforma data in file .txt
il passsaggio preliminare che mi mancava è il fatto che la funzione match() mette comunque tutta la stringa matchata in arr[0]
quanto al resto,
awk, per chi non lo mastica regolarmente, si presta se non all'offuscamento almeno alla confusioneVolevo solo essere un po' esoterico
(gelata)
- vaeVictis
- Imperturbabile Insigne
- Messaggi: 4703
- Iscrizione: venerdì 27 luglio 2012, 17:58
- Desktop: Gnome
- Distribuzione: Ubuntu 20.04 64bit
Re: Cerca e trasforma data in file .txt
In un messaggio aggiunto a questa discussione ieri (e giustamente quarantenato) si faceva riferimento a Python.
Dal momento che, a seguito di quel messaggio, avevo iniziato a sviluppare uno script in Python e ho finito di testarlo oggi, lo allego.
Codice: Seleziona tutto
#! /usr/bin/env python3
import argparse
import os
import locale
locale.setlocale(locale.LC_TIME, "it_IT.utf8")
import signal
import re
from sys import exit, stdin, stdout
from tempfile import NamedTemporaryFile
from datetime import datetime
def signal_handler(signal, frame):
print()
exit(0)
def file_path(path):
if os.path.isfile(path):
return path
else:
raise argparse.ArgumentTypeError(f"{path} is not a valid path")
def reformatDate(match):
myDate = match.group()
try:
return datetime.strptime(myDate, '%y-%m-%d').strftime("%A, %d %B %Y")
except ValueError:
return myDate
signal.signal(signal.SIGINT, signal_handler)
parser = argparse.ArgumentParser(description='Change date in format yy-mm-dd to format "day name, dd month name YYYY')
parser.add_argument("--input", dest="inFilePath", type=file_path, help="Input file")
parser.add_argument("--suffix", dest="suffix", help="Suffix for the backup file")
group = parser.add_mutually_exclusive_group()
group.add_argument("--output", dest="outFilePath", help="Output file")
group.add_argument("--inplace", dest="inplace", default=False, action='store_true', help="Flag to modify the input file inplace")
args = parser.parse_args()
if args.inplace and (args.inFilePath is None):
parser.error("--inplace requires --input.")
inFile = open(args.inFilePath) if args.inFilePath else stdin
if not args.inplace:
outFile = open(args.outFilePath, 'w') if args.outFilePath else stdout
else:
outFile = NamedTemporaryFile(dir='.', mode="w", delete=False)
r = re.compile(r'([0-9]{2}-[0-9]{2}-[0-9]{2})')
for line in inFile:
outFile.write(r.sub(reformatDate, line))
if inFile is not stdin:
inFile.close()
if outFile is not stdout:
outFile.close()
if args.inplace:
if args.suffix:
os.rename(args.inFilePath, args.inFilePath + args.suffix)
os.rename(outFile.name, args.inFilePath)
La gestione di questo aspetto è la stragrande maggioranza delle istruzioni dello script.
Ci sono alcuni punti che sono non molto pythonici, ma li ho lasciati così (mi riferisco al modo di aprire gli stream di input e output)
Pertanto, per avere l'help dello script:
Codice: Seleziona tutto
$ ./mioScriptPython.py -h
usage: mioScriptPython.py [-h] [--input INFILEPATH] [--suffix SUFFIX]
[--output OUTFILEPATH | --inplace]
Change date in format yy-mm-dd to format "day name, dd month name YYYY
optional arguments:
-h, --help show this help message and exit
--input INFILEPATH Input file
--suffix SUFFIX Suffix for the backup file
--output OUTFILEPATH Output file
--inplace Flag to modify the input file inplace
Modalità interattiva: si scrive sul terminale e lo script modifica al volo quanto scritto. Per chiudere, ctrl+c
Codice: Seleziona tutto
$ ./mioScriptPython.py
ads lladas
ads lladas
12-05-15
martedì, 15 maggio 2012
15-06-18 sd aasd 18-06-15
giovedì, 18 giugno 2015 sd aasd venerdì, 15 giugno 2018
^C
Codice: Seleziona tutto
$ ./mioScriptPython.py --output fileOutput.txt
Codice: Seleziona tutto
$ ./mioScriptPython.py >> fileOutput.txt
Si indica un file di input, e l'output viene stampato sullo standard output:
Codice: Seleziona tutto
$ ./mioScriptPython.py --input pathDelFileInput
Si indica il file di input e il file di output:
Codice: Seleziona tutto
$ ./mioScriptPython.py --input pathDelFileInput --output pathFileOutput
Volendo, per scrivere l'output su file, si può usare la redirezione come visto precedentemente.
3)
Modifica inplace:
Codice: Seleziona tutto
$ ./mioScriptPython.py --input pathDelFileInput --inplace
Modifica inplace e produzione del file di backup
Codice: Seleziona tutto
$ ./mioScriptPython.py --input pathFileInput --inplace --suffix SUFFIX
Ovviamente, le opzioni suffix e output sono mutualmente esclusive.
Confronto con lo script di gawk
I tempi di esecuzione sono approssimativamente gli stessi.
C'è però una DIFFERENZA.
Mi sono accorto che una data potrebbe essere formattata male.
Mi sto riferendo al fatto che, dandola nel formato yy-mm-dd, potrebbe verificarsi il caso in cui mm sia maggiore di 12 o dd indichi un giorno maggiore del massimo giorno disponibile per un dato mese.
Questo script gestisce la cosa lasciando quella data problematica invariata.
Lo script di gawk, per via di come funzionano le funzioni coinvolte al suo interno, funziona in questo modo un po' bizzarro:
Codice: Seleziona tutto
$ gawk -f mioScriptAwk.awk
12-04-30
lunedì, 30 aprile 2012
12-04-31
martedì, 01 maggio 2012
12-12-31
lunedì, 31 dicembre 2012
12-15-31
domenica, 31 marzo 2013
Si può modificare questo comportamento? Sì.
Mi va? No
Se dovesse essere un problema, si risolve con lo script Python in questo messaggio.
Il motivo per cui è difficile risolvere il problema dello script awk è dovuto al fatto che, visto che la modifica è interna al ciclo while, di sicuro non si può lasciare la data problematica invariata, poiché al successivo ciclo while sarebbe di nuovo "matchata" quella data per essere modificata, ma non sarebbe quindi modificata in quanto problematica e si entrerebbe in un loop infinito.
Si potrebbe risolvere modificando in qualche modo la struttura della data, ma anche in questo caso si dovrebbero fare dei controlli sulla data abbastanza complessi. Si capisce infatti facilmente se il mese dovesse sforare, ma per il controllo sul giorno la questione risulterebbe più complessa.
Pertanto, se la questione delle date sballate dovesse essere un problema, è meglio ricorrere allo script in Python .
«I fear not the man who has practiced 10000 kicks once,
but I fear the man who has practiced one kick 10000 times.»
- crap0101
- Rampante Reduce
- Messaggi: 8242
- Iscrizione: martedì 30 ottobre 2007, 6:33
- Desktop: LXDE
- Distribuzione: Ubuntu 18.04.1 LTS
- Sesso: Maschile
- Località: TO
- Contatti:
Re: Cerca e trasforma data in file .txt
Una soluzione che mi viene in mente per questo caso specifico potrebbe essere fare il controllo inverso sulla nuova data ottenuta:
Codice: Seleziona tutto
function check_date (date, new_date) {
#new_date: qualcosa tipo "domenica, 31 marzo 2013"
r = match(new_date,
/\w+, ([0-9]{2}) (\w+) ([0-9]{2})([0-9]{2})/,
a)
maybe_orig = sprintf ("%d-%02d-%d", a[4], months[a[2]], a[1])
#printf ("check_date: date: %s | new_date: %s | %s\n", date, new_date, maybe_orig)
return maybe_orig == date
}
BEGIN {
m = 60*60*24*28
for (x = 1; x <= 12; x++)
months[strftime ("%B", m*x)] = x
#for (v in months) print v, months[v]
}
{
match($0, /([0-9]{2})-([0-9]{2})-([0-9]{2})/, arr)
date = arr[0]
arr[1] = (arr[1] < 70 ? 20 : 19) arr[1]
time = sprintf("%s %s %s 1 0 0", arr[1], arr[2], arr[3])
secs = mktime(time)
new_date = strftime("%A, %d %B %Y", secs)
#gsub(date, new_date)
c = check_date(date, new_date)
printf ("date: %s | new_date: %s | %s\n",
date, new_date, c ? "OK" : "ERR")
}
# crap0101@orange:/tmp/foo$ echo 12-15-31 | awk -f a.awk
# date: 12-15-31 | new_date: domenica, 31 marzo 2013 | ERR
# crap0101@orange:/tmp/foo$ echo 12-12-31 | awk -f a.awk
# date: 12-12-31 | new_date: lunedì, 31 dicembre 2012 | OK
# crap0101@orange:/tmp/foo$
- Ricorda le ultime parole di suo padre: «Sta' alla larga dalle chiese, figlio. La sola cosa per cui hanno la chiave è il merdaio. E giurami che non porterai mai un distintivo della legge» - W.S. Burroughs
- vaeVictis
- Imperturbabile Insigne
- Messaggi: 4703
- Iscrizione: venerdì 27 luglio 2012, 17:58
- Desktop: Gnome
- Distribuzione: Ubuntu 20.04 64bit
Re: Cerca e trasforma data in file .txt
Sì, ci avevo pensato, infatti.anche se sarebbe meglio fare questo genere di controllo a parte
Stavo pensando di buttare giù al volo uno script per fare un controllo preliminare.
Sto un po' incasinato, però, e non so se riesco a farlo in tempi brevi.
Anche se, con Python, la questione è di facile soluzione, perché basta intercettare l'eccezione in caso di data sballata, e magari mandare in output la riga del file e la data sballata.
Se riesco, lo posto nella prossima settimana.
«I fear not the man who has practiced 10000 kicks once,
but I fear the man who has practiced one kick 10000 times.»
- steff
- Moderatore Globale
- Messaggi: 40301
- Iscrizione: domenica 18 febbraio 2007, 19:48
- Desktop: LXQt+labwc
- Distribuzione: Arch; Debian; Ubuntu Server
- Sesso: Maschile
- Località: Toscana
- Contatti:
Re: [Risolto]Cerca e trasforma data in file .txt
La Documentazione da consultare e la FAQ sul uso del forum
Sistemi: LXQt - semplice, modulare e configurabile + *ubuntu in Vbox
- UbuNuovo
- Imperturbabile Insigne
- Messaggi: 4446
- Iscrizione: sabato 12 dicembre 2009, 20:58
- Desktop: Mate
- Distribuzione: Ubuntu Mate 22.04.1 LTS
- Sesso: Maschile
- Contatti:
Re: [Risolto]Cerca e trasforma data in file .txt
Ricordiamocelo per il futuro, mi riferisco sopratutto alle sostituzioni di testo con AWk.
Chi c’è in linea
Visualizzano questa sezione: 0 utenti iscritti e 24 ospiti