Newsgroup: it.comp.lang.perl
Titolo: contenuto tra tags

Aiuto espressione regolare:
come faccio a leggere in Perl (credo con espressione regolare) il 
contenuto tra i tags: <tag></tag>
ad esempio:
apro un file e la prima riga č:
<ciao>Saluti!</ciao>
devo prendere il valore che č "Saluti!"
(naturalmente tra <tag></tag> non ci saranno mai caratteri tipo < o > 
che saranno tradotti nei rispettivi codici prima di memorizzare il 
valore nei tags)
Grazie,

"Larry"

#!/usr/local/bin/perl
if ($content =~ /<tag>([\w]*)<\/tag>/)
{
print "$1\n";
}
questo dovrebbe stamparti su stdout il contenuto dei tags che č nella
variabile $1. Poi facci quello che vuoi.
Se oltre ai caratteri alfanumerici e _ ci vuoi qualche altro carattere,
inserisci il o i caratteri nella parentesi quadra dopo la w.
ciao,
sub


io voglio leggere tutto....

 io voglio leggere tutto....
Mi serve un'altro bel'aiuto!
come potrei scandire il contenuto di vari <tag></tag> contenuti in un 
file?
Ad esempio:
<tag>
 <name>Larry</name>
 <mail>Larry@wall</mail>
</tag>
<tag>
 <name>Bill</name>
 <name>
Bi...@gates.com
</name>
</tag>
etc...
e magari come potrei prendere i valori contenuti nell'ultima coppia di 
<tag></tag> del file? (in fondo al file...)
Grazie a tutti ancora!!!!!

 [...]
 
http://search.cpan.org/~ovid/HTML-TokeParser-Simple-2.2/Simple.pm
"Give a man a fire and he's warm for a day, 
but set him on fire and he's warm for the rest of his life."
    --- Jingo, pg 181 by Terry Pratchett
        (Also the Monastery unofficial motto)
ciao,
S.
-- 
Stefano Rodighiero      |  
http://larsen.perlmonk.org
mailto:
lar...@perl.it
   |  
http://www.perl.it

,
 
stefano.r...@tiscali.it
 (Stefano Rodighiero) wrote:
grazie mille ma non posso installare moduli...

 grazie mille ma non posso installare moduli...
Dannaz.
Ti posso chiedere per quale motivo non puoi ?
Nel frattempo, qui potresti trovare qualche indicazione 
per installare lo stesso dei moduli nello spazio in cui 
puoi intervenire:
 
http://www.perlmonks.org/index.pl?node_id=128077
  ("I don't have permission to install a module 
    on the system!")
L'idea sarebbe di installare il modulo in una directory
utente della propria macchina di sviluppo, e poi uppare
(scusa il termine) tale directory in produzione.
Secondo me i salti mortali richiesti per far funzionare 
questo meccanismo sono molto meno mortali di quelli che
saresti costretto a compiere per fare il parsing a mano
di file HTML. 
Inoltre e` una tecnica che si ricicla in continuazione, 
e ti permetterebbe di adoperare liberamente uno degli 
strumenti piu` potenti che ha il Perl: CPAN.


Ti ringrazio!
Il link che mi hai postato mi ha poi portato ad altri liks 
interesantissimi!
La verità è che non voglia di installare moduli esterni ma di fare tutto 
da solo...ecco il codice che mi è venuto in mente:
open LEGGI , "<0000001.topic";
  $topic_content = join '' , <LEGGI>;
close;
while ( $topic_content =~ /<post>(.*?)<\/post>/sg )
{
  $post_content = $1;
  $post_counter ++;
   if ( $post_content =~ /<user>(.*?)<\/user>/sg ) { $user = $1; }
   if ( $post_content =~ /<mess>(.*?)<\/mess>/sg ) { $mess = $1; }
}
che ti sembra?
questo è il contenuto del file "0000001.topic"
<post>
  <user>Larry</user>
  <mess>Hello world</mess>
</post>
<post>
  <user>Bill</user>
  <mess>Keep off your fuckin' hands...</mess>
</post>
etc...
ora (come ti ho detto prima) mi sevirebbe un'altro modo per leggere 
(quandi mi serve)
solo la prima prima coppia di <post></post> e l'ultima coppia...
grazie ancora... (in futuro discuteremo di come suddividere tutto in più 
pagine!)

da solo...ecco il codice che mi è venuto in mente:
Come ho già detto altre volte, il problema a fare tutto da soli con
formati complessi come xml è che ci si trova prima o poi a fare da
soli uno scatafascio di cose, sostanzialmente duplicando quei moduli
che non si sono usati.
Reinventare la ruota può essere un'attività divertente, ma se, per far
questo, è necessario reinventare tutta la filiera petrolchimica,
dall'estrazione e trasporto del petrolio alla produzione dei
pneumatici, l'impresa rischia di diventare un po' troppo onerosa.
Comunque, se il documento da analizzare è davvero così semplice, e se
si può fare affidamento che sia sempre così (diciamo che si possa far
conto su di un semilavorato da rifinire, piuttosto che partire da
zero), si può tentare qualche approccio estremamente semplificato, del
genere:
        open LEGGI, '<0000001.topic';
        sub post;
        sub user;
        sub mess;
        my $num;
        while (<LEGGI>) {
                next if (/^\s*$/);
                chomp();
                post if (/^\s*<post>/i);
        }
        close LEGGI;
        sub post {
                print 'post: ', ++$num, "\n";
                while (<LEGGI>) {
                        next if (/^\s*$/);
                        user if (/^\s*<user>/i);
                        mess if (/^\s*<mess>/i);
                        last if (m|^\s*</post>|i);
                }
                print "\n";
        }
        sub user {
                s/^\s*<user>//i;
                s|</user>\n*||i;
                print 'user: ', $_, "\n";
        }
        sub mess {
                s/^\s*<mess>//i;
                s|</mess>\n*||i;
                print 'mess: ', $_, "\n";
        }
Funziona, naturalmente, solo se i tag <post> e </post> sono, come
nell'esempio, su righe isolate. Con qualche garanzia ulteriore sulla
regolarità del documento (per esempio, che il tag <post> sia sempre
tutto minuscolo) si può fare anche così:
	sub post;
	sub user;
	sub mess;
	my $num;
	$/ = '</post>';
	open LEGGI, '<0000001.topic';
	while (<LEGGI>) {
		next if (/^\s*$/);
		chomp();
		s/^\n*<post>\n//;
		post(split(/\n/));
	}
	close LEGGI;
	sub post {
		print 'post: ', ++$num, "\n";
		for $_ (@_) {
			next if (/^\s*$/);
			user if (/^\s*<user>/i);
			mess if (/^\s*<mess>/i);
			last if (m|^\s*</post>|i);
		}
		print "\n";
	}
Le sub user e mess sono le medesime sopra.
Il meccanismo è, s'intende, estremamente fragile: un granellino di
polvere qui o là e tutto s'inceppa. Ci lamenteremo, nel caso, con la
ditta che produce i semilavorati, chiedendo un maggior controllo sulla
qualità.
Se tutto va bene, risponde anche, in qualche modo (vedrai tu se più o
meno soddisfacente) al problema di prendere solo il primo, o
l'ennesimo, post: li leggi uno alla volta e ti fermi quando hai
finito.
Ciao.

 
Menomale che in Perl è facile fare le cose facili!
cmq io non voglio reinventare niente...ma la colpa è mia che sicuramente 
avrei potuto spiegarmi meglio: il file 00000.topic sarà solo strutturato 
cosi':
<post>
<user>Larry</user>
<mess>Hello world</mess>
</post>
<post>
<user>Bill</user>
<mess>Keep off your fuckin' hands...</mess>
</post>
etc...
i tags sono definiti...non si incontreranno mai tag diversi da quelli 
che ti ho elencato...la struttura è 
definita...(<post></post><user></user><mess></mess>quindi non ho bisogno 
di moduli esterni ma di una piccola funzione di scansione del file ed 
estrazione dei valori...
naturalmente nei valori contenuti tra i tags non ci saranno mai valori > 
o < (che interferirebbero con la funzione di estrazione...) ma verranno 
tradotti nei rispettivi codici...
That's it!

estrazione dei valori...
E in che cosa la piccola funzione di scansione che ho proposto non
risponde alle tue esigenze?
E` abbastanza chiaro che, impostando "$/ = '</post>';", ogni
operazione di lettura dal file restituisce un intero elemento post
alla volta (non era quello che chiedevi?)?
	$/ = '</post>';
	open LEGGI, '<00000.topic';
	while (<LEGGI>) {
		next if (/^\s*$/);
		chomp();
		s/^\s*<post>\s*//;
                print 'post numero ', ++$num, ': [', $_, "]\n";
	}
	close LEGGI;
(Sempre nell'assunzione non solo che la struttura sia sempre la
stessa, ma che i tag <post> e </post> siano scritti sempre in
minuscolo, e che </post> sia sempre su una riga isolata.)
Le successive funzioni post, user e mess sono solo più generali (alla
stampa puoi sostituire la memorizzazione in variabili o qualsiasi
altra cosa), ma non più complesse, delle if del tuo ciclo while, e in
realtà eseguono la medesima analisi.
Ciao.
[1] Carina la trovata del 'close;'.

...però con il tuo metodo il file mi rimane aperto più a lungo e col mio 
metodo i tag <post></post> non devono perforza stare su un rigo 
isolato...
grazie mille!

isolato...
E allora perché chiedi di poter leggere solo il primo elemento? O
leggi tutto insieme o leggi una cosa alla volta.
Quanto alla restrizione sulla struttura del documento, Perl, a
differenza di Awk, non ha la possibilità di usare un'espressione come
delimitatore di record, ma la cosa potrebbe essere risolta migliorando
il parser. Naturalmente, questo condurrebbe ad un codice più complesso
(ho già detto che analizzare un documento xml è una faccenda
complicata?).
Se vuoi leggere tutto insieme, comunque, non c'è necessità di leggere
prima in un contesto di lista, e fare un join dell'array risultante;
si può fare tutto in una volta con:
   open LEGGI , "<0000001.topic";
   local $/ = undef;
   $topic_content = <LEGGI>;
   close;
Neppure è necessario eliminare gli a capo, dal momento che usi il
modificatore /s.
A questo punto, puoi raccogliere tutti gli elementi post in un array,
in una botta sola:
    @posts = ($topic_content =~ /<post>(.*?)<\/post>/sg);
Anche gli if sono superflui; puoi risolvere così:
    ($user) = ($post_content =~ /<user>(.*?)<\/user>/sg);
    ($mess) = ($post_content =~ /<mess>(.*?)<\/mess>/sg);
Mettendo tutto insieme:
    open LEGGI, '<0000001.topic';
    local $/ = undef;
    my $topic_content  = <LEGGI>;
    close LEGGI;
    my $num_post;
    my @posts = ($topic_content =~ /<post>(.*?)<\/post>/sg);
    for my $post_content (@posts) {
        ($user) = ($post_content =~ /<user>(.*?)<\/user>/sg);
        ($mess) = ($post_content =~ /<mess>(.*?)<\/mess>/sg);
        print "##\npost ", ++$num_post;
        print "\nuser: ", $user;
        print "\nmess: ", $mess;    
        print "\n##\n";
    }
Ciao.

 wrote:

I miei complimenti...te ne sei uscito con un bel pezzo di codice...ma 
era sottointeso che tu sapevi fare coding meglio di me...in particolare 
ha risolto questi problemi:
$numero_replies = ( (scalar(@posts) - 1); #questo perchè il primo post 
non è una reply...
$primo_post = $posts[0]; # ottengo solo il primo post...
$ultimo_post = $#posts; # ottengo l'ultimo post...
correggimi se ho sbagliato qualcosa...
ancora grazie!
(ultima cosa...ma dopo il close del file $/ torna normale o rimane 
indefinita?);
Ciao!!

> $ultimo_post = $#posts; # ottengo l'ultimo post...
pardon...
pop(@posts);

il parser.
Perché poi ho tirato fuori la storia della riga isolata? non è vero
per nulla: impostando il separatore di record a '</post>' viene letto
un "record" alla volta, indipendentemente dal fatto che '</post>' sia
o meno su una riga isolata. L'unica limitazione è data dal fatto che
sia scritto letteralmente così (non '</POST>' o '</Post>' o altre
combinazioni che normalmente si ritengono equivalenti). Il record
risultante, naturalmente, conterrà tutti gli a capo eventualmente
catturati.
Ciao.

indefinita?);
Come ogni variabile local, torna "normale" (o meglio, al suo valore
precedente), al termine del blocco in cui è localizzata. Nell'esempio
pubblicato, l'intero script, ma è possibile ridurre il blocco alla
sola lettura del file:
     my $topic_content;
     {
         open LEGGI, '<0000001.topic';
         local $/ = undef;
         $topic_content  = <LEGGI>;
         close LEGGI;
     }
Purtroppo, non è possibile usare una variabile lessicale, ma ci sono
altri modi, oltre a questo, per limitare l'intervallo di definizione e
di esistenza di queste variabili. Puoi leggere al riguardo la
documentazione sui file handle.
Ciao.

 delimitatore di record, [...]
non direttamente, ma tramite File::Stream che trovi su CPAN sì:
   use File::Stream;
   my $stream = File::Stream->new($filehandle);
   $/ = qr/\s*,\s*/;
   print "$_\n" while <$stream>;
cheers,
Aldo