P4A3 framework: un widget orologio che utilizza jClock e jQuery

Widget p4a clockHo approfittato della necessità di presentare un piccolo orologio in una maschera di un progetto al quale sto lavorando, per scrivere l’integrazione del plugin javascript jClock sviluppato con jQuery nel framework P4A 3.
Nulla di spettacolare, siamo d’accordo, ma la scelta è caduta su jClock perché P4A supporta nativamente jQuery, e mi è sembrata la cosa più logica seguire quella direzione.

Che cosa fa?

Mostra un orologio digitale con l’ora del client, configurabile nell’aspetto e nel comportamento attraverso opportuni metodi.

Elenco dei metodi:
NomeTipoDefaultNote
setTimeNotation()string24hValori possibili 12h o 24h. Determina il modo di visualizzazione dell’orario
setAm_pm()stringfalseSe impostato a “true”, mostra A.M./P.M. (Solo se timeNotation() = “12h”).
setUtc()stringfalseSe impostato a “true”, mostra il tempo utilizzando UTC.
setUtc_offset()integer0Valori possibili: da -12 a +12, imposta l’offset da UTC.
setFontFamily()stringNULLSe specificato, imposta il set di caratteri per lo stile CSS, altrimenti eredita.
setFontSize()stringNULLSe specificato, imposta la dimensione in pixel dei caratteri per il testo (p.e.: “14px“), altrimenti eredita.
setForeground()stringNULLSe specificato, imposta il colore del testo (p.e.: “white“), altrimenti eredita.
setBackground()stringNULLSe specificato, imposta lo sfondo del testo (p.e.: “#4b718a“), altrimenti eredita.

Il codice per inserire l’orologio in una maschera di esempio:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class test_clock extends p4a_base_mask
{
 public function __construct()
 {
   parent::__construct();
   $this->setTitle("test Clock");
   $clock = $this->build("P4A_jClock","clock");
   $clock->setStyleProperty("width","150px");
   $clock->setStyleProperty("float","none");
   $clock->setStyleProperty("margin","auto");
   $clock->setTimeNotation("12h");
   $clock->setAm_pm(true);
   $clock->setFontSize("14px");
   $clock->setForeground("white");
   $clock->setBackground("#4b718a");
   $this->frame->anchorCenter($clock);
  }
}

Il pacchetto completo della classe, della libreria javascript e della maschera di esempio è scaricabile qui

Note:

Al momento, il sistema di scrittura del codice detto “chainability” non funziona solo con i metodi che ho dichiarato nella classe P4A_jClock. Mi riprometto di individuare e correggere l’errore! Sarà molto gradito il feedback di tutti coloro che volessero utilizzare questo widget, per individuare e correggere eventuali altri errori e per apportare nuovi miglioramenti.

Conclusioni:

E’ interessante soprattutto capire come scrivere widget per P4A. Infatti, sebbene P4A framework, particolarmente nella nuova versione, offra già parecchie feature, capita comunque di aver necessità di qualcosa di particolare per le proprie applicazioni!
In futuro potrebbe essere interessante creare un widget per un orologio analogico, ne ho visti di molto belli qui: CoolClock – The Javascript Analog Clock. e già ci sto facendo un pensierino…

Riferimenti ed approfondimenti:


Fabrizio Balliano ha scritto:

Ciao Mario, per “abilitare” la chainability devi semplicemente aggiungere un “return $this” alla fine di ogni metodo della classe, tutto qui :-))) ottimo widget comunque soprattutto per l’utilizzo “non mascherato da p4a” di jquery!
02.07.08 09:00
Mario Spada ha scritto:

Grazie Fabrizio! Codice corretto, adesso funziona anche la chainability per i metodi dichiarati nel widget.
07.07.08 18:34

PHP: un datagrid XHTML valido con paginazione per applicazioni web

Esempio di datagrid
Esempio di datagrid

Questo datagrid è un aggiornamento di quello presentato in questo post. Ho apportato alcune sostanziali modifiche per renderlo più flessibile ed accessibile, pur mantenendo le stesse prerogative che mi ero imposto inizialmente.
La modifica più urgente era quella di implementare un sistema di paginazione. Questo ha comportato inevitabilmente il cambiamento della modalità di passaggio dei dati al costruttore della classe. Mentre prima era sufficiente fornire il riferimento al risultato di una query, adesso è necessario passare il link alla connessione, il nome del database ed infine la query in formato stringa.

Le caratteristiche iniziali, che sono state ulteriormente migliorate, sono:

  • XHTML valido
  • Personalizzabile tramite fogli stile CSS
  • colonna aggiuntiva di link per azioni tipo “modifica”
  • Aspetto gradevole e alternanza di colorazione delle singole righe
  • Compatibile sia con PHP4 che con PHP5

Le nuove caratteristiche:

  • Semplice sistema di paginazione nel <tfoot> della tabella. Il numero di record per pagina da visualizzare vengono impostati come parametro del costruttore, default=0
  • Autoriconoscimento della o delle chiave primarie. Nel caso in cui l’autoriconoscimento fallisca, è possibile impostarle manualmente come parametro del costruttore
  • Il link nella colonna aggiuntiva può contenere una querystring, in questo caso il link alla chiave primaria viene appeso con “&” altrimenti con “?”
  • Possibilità di utilizzare il datagrid in una pagina che già contiene una querystring. In questo caso è necessario passare la querystring esistente come parametro al costruttore.
  • Possibilità di impostare numerose proprietà dei tag HTML

I parametri del costruttore:

  1. $link (obbligatorio) – riferimento alla connessione al db
  2. $db (obbligatorio) – stringa con il nome del database
  3. $query (obbligatorio) – stringa contentente la query di tipo SELECT
  4. $recPerPage=0 – Numero di record per pagina. Default=0 (tutti i record senza paginazione)
  5. $pk = NULL – Nome della chiave primaria (se più di una, separate da virgola) da immettere manualmente, se NULL (default) il riconoscimento è automatico
  6. $qs = NULL – Stringa contenente l’eventuale querystring associata alla pagina in cui viene pubblicato il datagrid.
Elenco dei metodi:
NomeTipoDefaultNote
setPkIsVisible()booleanfalseDetermina se le colonne relative alla chiave primaria sono visibili o no
setLinkPage()stringNULLImposta il link alla pagina da richiamare per le modifiche (Riferimento alla chiave primaria). Se NULL, la colonna aggiunitva non viene visualizzata.
setLinkLabel()string“Edit”Imposta il testo da visualizzare nella colonna dei link. Può contenere codice HTML per caricare un’icona.
setLinkTitle()string“Edit”Imposta il titolo del tag <a> nella colonna dei link.
setPrev()string“prev”Imposta il testo da visualizzare per la paginazione indietro. Può contenere codice HTML per caricare un’icona.
setNext()string“next”Imposta il testo da visualizzare per la paginazione in avanti. Può contenere codice HTML per caricare un’icona.
setSummaryTable()string“Data table”Imposta il testo per la descrizione “summary” della tabella.
setCaptionTable()string“Table”Imposta il testo per l’intestazione della tabella.
makeTable()Stampa la tabella

Un esempio di utilizzo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Database connection params
$host = 'localhost';
$user = 'root';
$password = '';
$database = 'prova';
$query = 'select * from es_libri';
// open the connection to the db server
$link = mysql_connect($host,$user,$password);
include ('datagrid.class.php');
// datagrid class with some options
$mydatagrid = &amp; new datagrid($link,$database,$query,3); // 3 rec per page
$mydatagrid-&gt;setLinkPage("test.php");
$mydatagrid-&gt;setLinkLabel("<img src="./mod.gif" alt="edit" />");
$mydatagrid-&gt;setLinkTitle("edit");
$mydatagrid-&gt;setPrev("<img src="./prev.gif" alt="previous" />");
$mydatagrid-&gt;setNext("<img src="./next.gif" alt="next" />");
$mydatagrid-&gt;setPkIsVisible(true);
$mydatagrid-&gt;setSummaryTable("Elenco dei libri in biblioteca del settore informatica");
$mydatagrid-&gt;setCaptionTable("Elenco libri informatica");
$mydatagrid-&gt;makeTable();

Per vedere l’esempio, cliccare qui.
Questo, invece è il pacchetto contenente la classe, il file di esempio, il foglio stile, le gif e l’sql per la creazione della tabella “es_libri”, (da creare in un db chiamato “prova”).

Note:

Per quanto abbia fatto diverse prove, non posso dire di aver coperto tutti i possibili errori, fate qualche prova prima di metterlo in produzione!.

Conclusioni:

Per il futuro mi riprometto di aggiungere la caratteristica di autoriconoscimento dei tipi dei campi e la formattazione automatica con allineamento a destra per i numeri

Riferimenti ed approfondimenti:

P4A3 framework: un helper per pulire la directory tmp in ambiente Windows

P4A 3 Logo

Ho scritto questo piccolo “helper” per ovviare ad un piccolo inconveniente che avviene con P4A3, esclusivamente in ambiente Windows. Succede che, se viene utilizzata la funzione: P4A_Output_File(), ad esempio per generare un file pdf, il file temporaneo non viene più cancellato. Ho potuto verificare che in ambiente Linux questo non succede, e tutti i file generati vengono eliminati una volta passati al browser.

Non sono riuscito a capire esattamente il motivo ma credo che il problema risieda ancora una volta nel differente trattamento da parte di Windows dei separatori nei percorsi assoluti dei path (Windows utilizza i \ invece dei /).
Il problema, anche se può risultare fastidioso alla lunga, per l’accumularsi di un eccessivo numero di files nella directory /uploads/tmp, non è di certo grave, e soprattutto, è molto semplice da risolvere. E’ sufficiente infatti lanciare un metodo che ripulisca” la cartella dai file temporanei più vecchi di n ore (o giorni, o minuti).

Questo è il codice della funzione, che per comodità può essere gestito come “helper”, ovvero un metodo personalizzato prontamente disponibile da qualsiasi maschera della nostra applicazione:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function p4a_mask_clean_old_tmp_files($obj, $olderThan)
{
 $fileDeleted = 0;
 $expire_time = empty($olderThan[0]) || !is_numeric($olderThan[0])? 24: $olderThan[0];
 $fileTypes = array("_p4a_*","cache_*");
 if (P4A_OS == 'windows') {
  define('TEMPDIR','\tmp\\');
  $dirToClean =  P4A_UPLOADS_DIR.TEMPDIR;
  foreach ($fileTypes as $fileType){
   foreach (glob($dirToClean . $fileType) as $filename) {
   $fileCreationTime = filectime($filename);
   $fileAge = time() - $fileCreationTime;
    if ($fileAge > ($expire_time * 3600)){
     if (unlink($filename)){
      $fileDeleted++;
     }
    }
   }
  }
 }
 return $fileDeleted;
}

Il file si deve trovare nella directory /libraries all’interno della directory principale della nostra applicazione, e si deve chiamare: p4a_mask_clean_old_tmp_files.php. Il default è impostato su 24 ore.
Per cambiare la scala del tempo trascorso da ore a minuti è necessario cambiare questa riga:
if ($fileAge > ($expire_time * 3600)){
in
if ($fileAge > ($expire_time * 60)){

Il metodo viene richiamato in questo modo:

$res = clean_old_tmp_files(1); // cancella i files più vecchi di un'ora

La funzione restituisce il numero di files cancellati oppure 0

Conclusioni:

Ho già segnalato questa piccola anomalia a Fabrizio Balliano in questo commento, e credo che ben presto, magari con la release ufficiale di P4A3 annunciata per il 24/06/2008, il problema verrà risolto. Nel frattempo, gli utenti Windows, possono usare questo semplice “helper”.

Riferimenti ed approfondimenti:


Fabrizio Balliano ha scritto:

hum, io aveto testato e vedendo che funzionava correttamente non mi sono piu’ interessato alla questione, ma non sapevo che il problema fosse su windows… ora per martedi’ sara’ complicato risolverlo, vedo che posso fare.
22.06.08 11:42
Fabrizio Balliano ha scritto:

bug risolto :-)
22.06.08 13:04
Mario Spada ha scritto:

Grande Fabrizio, e a tempo di record!!

PHP e MySQL: una classe per stampare alberi gerarchici con liste annidate

Lista Modified Preorder Tree TraversalLa soluzione denominata “Modified Preorder Tree Traversal”, che mal si traduce in italiano, è probabilmente la soluzione più efficace per immagazzinare dati di tipo gerarchico in un database. La soluzione tipica alternativa è il modello a liste di adiacenza. Quest’ultimo modello soffre di alcune limitazioni e debolezze, come ad esempio la particolar cura che deve essere presa nella cancellazione di sotto-alberi che può portare a potenziali figli orfani, e la difficoltà di stesura delle query SQL.
In questo articolo mi riferirò quindi esclusivamente al modello Modified Preorder Tree Traversal. Questo modello è molto ben descritto in questo articolo di Mike Hillyer sul sito ufficiale MySQL nel paragrafo denominato: “Nested Set Model”. Il concetto chiave che sta alla base del modello è di non considerare più la struttura gerarchica come un insieme di nodi e di linee, ma come una serie di insiemi e sottoinsiemi annidati. Il concetto sarà più chiaro dopo aver letto l’articolo e la figura esplicativa della struttura, riportata qui sotto.
treeGraphLe varie query necessarie a gestire il database sono state riprese e tradotte in funzioni PHP da Daevid Vincent in questo thread su questo forum. Il lavoro di Daevid si è rivelato davvero prezioso, e mi ha fatto risparmiare davvero molto tempo, però non ero completamente soddisfatto del sistema di stampa HTML dell’albero. Infatti la funzione utilizza “blank space” per la rappresentazione dell’indentazione, mentre sarebbe più corretto ed elegante utilizzare liste annidate di tipo <UL>. Ho fatto quindi alcune modifiche alla funzione per raggiungere questo scopo.

Queste sono le query per creare il database campione e popolarlo con alcuni dati di esempio:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE nested_category (
 category_id INT AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(20) NOT NULL,
 lft INT NOT NULL,
 rgt INT NOT NULL
);
 
INSERT INTO nested_category
VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),
(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);

E questo il codice della classe modificata:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class preorderModelTree {
 
  var $dbConn;
 
  function preorderModelTree ($resConn) {
    $this-&gt;dbConn = $resConn;
  }
 
  function dbQuery($sql){
    $rs = @mysql_query($sql,$this-&gt;dbConn);
    return $rs;
  }
 
  function fetchRow($rs) {
    if ( $row = @mysql_fetch_array($rs,MYSQL_ASSOC) ) {
        return $row;
    } else {
        return false;
    }
  }
 
  function print_nested_tree_list($pageLink = "?id=", $id = 1)
  {
    $sth = $this-&gt;dbQuery("SELECT node.category_id as id, node.name AS name,
    (COUNT(parent.name) - 1) as indent
    FROM nested_category AS node, nested_category
    AS parent
    WHERE node.lft BETWEEN parent.lft
    AND parent.rgt  
    GROUP BY node.name
    ORDER BY node.lft");
 
    if ($sth) {
    $oldRowIndent = -1;
 
    while($row = $this-&gt;fetchRow($sth))
    {
      if($row['id'] == $id){
        echo("

“, $oldRowIndent-$row[‘indent’]); echo(”

“); echo str_repeat(”

“, $oldRowIndent); echo(”

“); } } }

Come si evince dal codice, la chiave di tutto è la query ben fatta. Il campo calcolato indent fornisce la misura dell’indentazione che è rappresentata dal numero di gusci in cui la categoria è compresa. Tramite questo valore è possibile costruire il corretto annidamento di elementi <ul> e <li>.

Il pacchetto contenente la classe, il codice sql e un file di esempio è scaricabile qui.

Un ringraziamento particolare per le “imbeccate” ed i consigli, all’amico e collega Alessandro Scoscia.

Conclusioni:

Un ulteriore miglioramento della rappresentazione dell’albero gerarchico potrebbe venire dall’utilizzo di JQuery. Tramite questa libreria javascript si potrebbe implementare un’azione tipo toggle con la quale mostrare o nascondere i sotto-alberi selezionati.

Riferimenti ed approfondimenti:


evilripper ha scritto:

preziossimo articolo!!
grazie per tutte le informazioni!!
ciao

Javascript: controllo di form con le espressioni regolari

Esempio di formL’esigenza era quella di trovare uno script client-side sintetico ed efficace nella validazione dei form HTML. Gli aggettivi sintetico ed efficace portano subito a pensare alle Espressioni regolari! Ora, sebbene devo dire che non sono certo un esperto di espressioni regolari, riconoscendo la loro potenza e la loro versatilità ho cercato di approfondire l’argomento.
Utilizzando questa sintassi, è possibile validare decine di campi di un form HTML, ricorrendo ad una sola funzione Javascript. E’ sufficiente, in un’altra funzione, passare come parametro il giusto pattern per ogni campo del form.
Per utilizzare le Espressioni regolari in Javascript ci sono due modi:

  1. Assegnazione letterale: var regExpression = /pattern/;
  2. Assegnazione mediante costruttore: var regExpression = new RegExp("pattern");/code>

In questo script ho utilizzato il primo metodo, anche se in effetti sarebbe preferibile il secondo che permette di risparmiare ulteriore codice in quanto il pattern può essere passato come stringa.
I metodi dell’oggetto RegExp sono 2:

  1. test(string) : esegue un test sulla stringa in base al pattern fornito e ritorna TRUE o FALSE se trova o meno la corrispondenza
  2. exec(string) : esegue una ricerca sulla stringa in base al pattern fornito e ritorna NULL se non c’è corrispondenza, oppure un array che contiene i risultati.

Per la costruzione del pattern, è necessario conoscere alcuni rudimenti delle Espressioni regolari. Vediamo un breve sommario:

Principali metacaratteri (caratteri speciali)

  • . indica qualsiasi carattere (escluso un a capo)
  • * indica zero o più occorrenze (di un carattere o di un gruppo di caratteri)
  • ? indica zero o una occorrenza (di un carattere o di un gruppo di caratteri)
  • {} (parentesi graffe): indicano il numero esatto, o minimo, o massimo, o l’intervallo di occorrenze (di un carattere o di un gruppo di caratteri)
  • + indica una o più occorrenze (di un carattere o di un gruppo di caratteri)
  • ^ indica l’inizio della stringa (o, se all’interno di una classe di caratteri, la negazione della stessa)
  • $ indica la fine della stringa
  • | indica l’operatore OR
  • \ carattere di escape dei caratteri speciali
  • () parentesi tonde, destinate a contenere una sottostringa
  • [] parentesi quadre, destinate a contenere una ‘classe’ di caratteri

Conoscendo il significato di questi metacaratteri, e con l’aiuto dei numerosi tutorial disponibili in rete (vedi sezione Riferimenti ed approfondimenti), è possibile almeno capire e modificare a nostro uso, i pattern già pronti per il controllo dei più comuni campi che possono essere presenti in un form.

Lo script che propongo utilizza pattern integralmente presi o leggermente modificati, tratti da questo documento

i campi sottoposti a verifica che ho preso in considerazione, e le regole per l’immissione dei dati sono:

CampoRegolaPattern
Utentelettere, numeri, e i segni . _ –/^([a-zA-Z0-9\.\_\-])+$/
Passwordmin 6, max 12 di caratteri, numeri, _ * – + ! ? , : ; . e lettere accentate/^[a-zA-Z0-9\_\*\-\+\!\?\,\:\;\.\xE0\xE8\xE9\xF9\xF2\xEC\x27]{6,12}/
Nomecaratteri, lettere accentate apostrofo e un solo spazio fra le parole/^([a-zA-Z\xE0\xE8\xE9\xF9\xF2\xEC\x27]\s?)+$/
C.A.P.5 numeri/^\d{5}$/
E-mailcaratteri e . _ % – + @ + caratteri compreso . + . + min 2, max 4 caratteri/^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/
Dataformato mm/gg/aaaa o mm-gg-aaaa o mm.gg.aaaa/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[-/.](19|20)\d\d/
Codice fiscalevedi regole su Wikipedia/^[a-zA-Z]{6}\d\d[a-zA-Z]\d\d[a-zA-Z]\d\d\d[a-zA-Z]/

Ecco il codice dello script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// JavaScript Document
//------------------------------------------------------------------------
// checkForm : checks each field for a given pattern of chars
function checkForm(frm){
  var pattern;
  // Utente (lettere, numeri, e i segni . _ -)
  pattern = /^([a-zA-Z0-9\.\_\-])+$/;
  if(!checkPatternChars('Utente',frm.user.value,pattern,true))
  {
   frm.user.focus();
   return false;
  }
  // Password (min 6, max 12 di caratteri, numeri, _ * - + ! ? , : ; .
  // e lettere accentate)
  pattern = /^[a-zA-Z0-9\_\*\-\+\!\?\,\:\;\.\xE0\xE8\xE9\xF9\xF2\xEC\x27]{6,12}/;
  if(!checkPatternChars('Password',frm.password.value,pattern,true))
  {
   frm.password.focus();
   return false;
  }
  // Nome (caratteri, lettere accentate apostrofo e un solo spazio fra le parole)
  pattern = /^([a-zA-Z\xE0\xE8\xE9\xF9\xF2\xEC\x27]\s?)+$/;
  if(!checkPatternChars('Nome',frm.name.value,pattern))
  {
   frm.name.focus();
   return false;
  }
  // C.A.P.  (5 numeri)
  pattern = /^\d{5}$/;
  if(!checkPatternChars('CAP',frm.cap.value,pattern))
  {
   frm.cap.focus();
   return false;
  }
  // E-mail
  pattern = /^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
  if(!checkPatternChars('E-mail',frm.email.value,pattern,true))
  {
   frm.email.focus();
   return false;
  }
  // Data formato mm/gg/aaaa o mm-gg-aaaa o mm.gg.aaaa
  pattern = /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[-/.](19|20)\d\d/;
  if(!checkPatternChars('Data',frm.data.value,pattern))
  {
   frm.data.focus();
   return false;
  }
  // Codice fiscale
  pattern = /^[a-zA-Z]{6}\d\d[a-zA-Z]\d\d[a-zA-Z]\d\d\d[a-zA-Z]/;
  if(!checkPatternChars('Codice fiscale',frm.cf.value,pattern))
  {
   frm.cf.focus();
   return false;
  }
  return true;
}
//------------------------------------------------------------------------
// checkPatternChars : checks a specific pattern of typed chars
function checkPatternChars(nm,vlu,pattern,required){
  if ( required === undefined ) {
      required = false;
   } 
  if(!required &amp;&amp; vlu==""){
    return true;
  }
  if (!pattern.test(vlu)){
    alert("Il campo "+nm+" non e\' valido!");
    return false;
  }
  else { 
    return true; 
  }
}

Un esempio è visibile qui il pacchetto è scaricabile qui

Conclusioni:

L’accoppiata javascript – Espressioni regolari fornisce un ottimo strumento di verifica client-side dei dati immessi dagli utenti. La verifica lato client, però, ha dei limiti e serve più che altro ad aiutare l’utente alla compilazione del form. Se si vuole essere davvero sicuri di avere dati corretti da passare alla query, è obbligatorio ripetere il controllo server-side, utilizzando magari gli stessi pattern e le corrispondenti funzioni PHP: ereg() ed eregi()

Riferimenti ed approfondimenti:

Uno script VBScript per la ricerca e sostituzione di un array di stringhe in un file di testo

Finestra di dialogo VBScriptTutti i programmatori hanno avuto a che fare con operazioni di cerca/sostituisci nel testo, e di utilities per questo tipo di operazioni, in effetti ce ne sono parecchie. Gli utenti Linux hanno a disposizione molti strumenti nativi potenti e flessibili, chi utilizza Windows invece, sebbene possa installare linguaggi di script di terze parti come Perl o PHP, se vuole utilizzare strumenti nativi deve utilizzare VBScript e Windows Script Host (WSH). Per essere precisi, non è del tutto necessario conoscere VBScript, perché la piattaforma WSH è language-indipendent, dunque altri linguaggi come javascript sono pure supportati.
Fatta questa piccola premessa, torniamo al tema delle sostituzioni nel testo. Ho trovato su Internet numerosi script per la ricerca e la sostituzione di testo sia in un unico file che in file multipli (problema comune per i webmaster), ma non ho trovato molto per poter sostituire velocemente una lunga lista di stringhe, con un’altra all’interno di un certo file di testo. Un esempio pratico potrebbe essere un file CSV che contiene una colonna di codici da sostituire con la corrispettiva lista di id estratta dalla tabella di un database. Questo processo potrebbe risultare utile per velocizzare l’importazione di certi dati in tabelle di database.

Ho assemblato questo piccolo script VBScript utilizzando porzioni di codice reperite nella rete, ed aggiungendo alcune personalizzazioni.
Queste sono le caratteristiche:

  • Lo script carica da un file CSV che utilizza la “,” come separatore, un array di stringhe da cercare e il corrispettivo array di stringhe sostitutive
  • Apre il file con il testo da elaborare ed effettua le sostituzioni
  • Salva il testo modificato in un nuovo file in modo da preservare l’originale

Ecco il codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
' VB Script Document
option explicit
Dim FileName, FileContents, dFileContents, FileFindReplace
Dim oFSO, oTextFile, arrList, items, keys
Dim sNextLine, tmp, i
 
FileName = WScript.Arguments(0)
FileFindReplace = WScript.Arguments(1)
 
'Read source text file
FileContents = GetFile(FileName)
dFileContents = FileContents
 
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oTextFile = oFSO.OpenTextFile(FileFindReplace, 1)
Set arrList = CreateObject("Scripting.Dictionary")
Do Until oTextFile.AtEndOfStream
    sNextLine = oTextFile.Readline
    tmp = Split(sNextLine , ",")
    arrList.Add  tmp(0), tmp(1)
Loop
items = arrList.Items
keys = arrList.Keys
'Loop through Items array
For i = 0 To UBound(items)
  'replace all string In the source file
  dFileContents = replace(dFileContents, items(i), keys(i), 1, -1, 1)    
Next
 
'Compare source And result
if dFileContents <> FileContents Then
  'write result If different
  WriteFile NewFileName(FileName), dFileContents
  Wscript.Echo "Replace done."
Else
  Wscript.Echo "Searched string Not In the source file"
End If
 
'Read text file
Function GetFile(FileName)
  If FileName<>"" Then
    Dim FS, FileStream
    Set FS = CreateObject("Scripting.FileSystemObject")
      on error resume Next
      Set FileStream = FS.OpenTextFile(FileName)
      GetFile = FileStream.ReadAll
  End If
End Function
 
'Write string As a text file.
Function WriteFile(FileName, Contents)
  Dim OutStream, FS
  on error resume Next
  Set FS = CreateObject("Scripting.FileSystemObject")
    Set OutStream = FS.OpenTextFile(FileName, 2, True)
    OutStream.Write Contents
End Function
 
'Compose new file name with date and time.
Function NewFileName(sFileName)
  Dim MyDate, arrNewFileName, sNewFileName, sExt
  arrNewFileName = Split(sFileName, "\")
  sNewFileName = arrNewFileName(Ubound(arrNewFileName))
  MyDate = Replace(Now, "/", "") 
  MyDate = Replace(MyDate, ".", "")
  MyDate = Replace(MyDate, " ", "_")
  If InStr(sNewFileName, ".") Then
    sExt = Right(sNewFileName, 3)
    sNewFileName = Left(sNewFileName,(Len(sNewFileName) - 4))
    sNewFileName = sNewFileName &amp; "_" &amp; MyDate &amp; "." &amp; sExt
  Else
    sNewFileName = sNewFileName &amp; "_" &amp; MyDate
  End If
  NewFileName = sNewFileName 
End Function

Vediamo la sua applicazione in un file di prova (prova.txt) che contiene il seguente testo:

uno
due
due
tre
tre
quattro
cinque
cinque
sei
sette
otto
otto
nove
dieci
dieci
dieci
dieci

nel quale vogliamo convertire le stringhe in numeri (per fare questo in realtà ci sarebbero anche altri metodi più veloci!)
Il file per le sostituzioni (decodificaprova.txt) sarà

1,uno
2,due
3,tre
4,quattro
5,cinque
6,sei
7,sette
8,otto
9,nove
10,dieci

lanciamo lo script che chiameremo find_replace.vbs, indicando come primo parametro il file su cui effettuare le sostituzioni e come secondo parametro il file di decodifica:
C:\percorso_del_file\find_replace.vbs" prova.txt decodificaprova.txt
Il risultato (in questo caso: prova_18052008_104851.txt) prende il nome del file originale + una stringa che rappresenta la data e l’ora di sistema:

1
2
2
3
3
4
5
5
6
7
8
8
9
10
10
10
10

NOTE:

Il file di decodifica deve avere una prima colonna con l’elenco delle sostituzioni ed una seconda colonna con l’elenco delle stringhe da ricercare. E’ molto importante evitare stringhe ambigue, ad esempio se si vuole sostituire l’articolo “la” con “una” e nel testo esiste la parola “lana”, questa verrà convertita in “unana”! In questi casi è bene inserire dei delimitatori come ad esempio il carattere #

Requisiti:

E’ necessario che sia installato il supporto WSH, la versione più recente è la 5.7

Conclusioni:

Lo script si è rivelato molto veloce ed efficiente, sarebbe interessante migliorarlo per ottenere una maggiore flessibilità e sicurezza, ma il suo scopo era quello di risolvere velocemente un problema che mi avrebbe fatto perdere molto tempo se affrontato manualmente, e lo ha raggiunto!

Riferimenti ed approfondimenti: