Archivi Categorie: JavaScript

JavaScript – stringhe e numeri – 2

Continuo da qui e finisco il rapido esame di Node.js con la valutazione se sia possibile usarlo per scripts veloci in alternativa a Python. Do subito la soluzione: sì se lo scripter è un ninjia per JavaScript o se lo script deve finire (modificato) anche nel Web. I miei validi  collaboratori  grokers Edo e PLG fanno notare che è prassi comune modificare gli script in ogni nuova istanza, a volte per pigrizia (es. nomi di files codificati nel codice), a volte per motivi più sensati (es. formattazione dell’output). Aspetta, non è una novità nuova: πάντα ῥεῖ.

Oltre a quanto raccontato nel post precedente (qui) restano (per le mie elaborazioni tipiche) da esaminare l’acquisizione di dati provenienti da fogli di calcolo tipo Excel, di solito il modulo Calc di Libre Office.

Node ha un modulo per gestire i file .xls e .xlsx:

# [SheetJS js-xlsx](http://sheetjs.com)

Parser and writer for various spreadsheet formats. Pure-JS cleanroom implementation from official specifications, related documents, and test files.
Emphasis on parsing and writing robustness, cross-format feature compatibility with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.

Il modulo è installabile con npm ma da noi non viene usato. È la stessa situazione già vista con il modulo xlrd di Python.

C’è, rispetto a quanto visto nel post indicato, solo un problema legato alla rappresentazione dei numeri floating point, in particolare i separatori decimale e delle migliaia. Quest’ultimo è in genere usato solo dai gestionali, na se del caso…

Il file di dati che uso come esempio contiene questi dati:

Come già visto in precedenza acquisisto il testo dal file salvato come .csv

* xls $ node
> var fs = require('fs')
undefined
> var dati = fs.readFileSync('prova.csv', 'utf8').split('\n')
undefined
> dati
[ 'test di lettura files excel-like,,,',
  '1,"2,3",quattro,5^6',
  '2,"3,4",,dopo campo vuoto',
  '' ]

OOPS! sbagliato: la virgola iene usata sia come separatore di campo che separatore decimale. Nella creazione del .csv occorre definire il separatore di campo a punto-virgola

e sarà tutto OK

> var dati = fs.readFileSync('prova-pv.csv', 'utf8').split('\n')
undefined
> dati
[ 'test di lettura files excel-like;;;',
  '1;2,3;quattro;5^6',
  '' ]

Adesso è giunto il momento di usare le regexs, ne basta una per riga, replace non funzione su più linee:

> var ok1 = dati[1].replace(/,/g, '.')
undefined
> ok1
'1;2.3;quattro;5^6'

JavaScript (quindi Node) è –come dire– poco rigoroso sul tipo di dato che sta elaborando. Per fortuna esiste la funzione typeof, una manna da usare tutte le volte che possono esserci ambiguità:

> i = 6 * 7
42
> r = 22 / 7
3.142857142857143
> st = 'ciao!'
'ciao!'
> ov = 1 / 0
Infinity
> e = Number('x')
NaN
> typeof(i)
'number'
> typeof(r)
'number'
> typeof(st)
'string'
> typeof(ov)
'number'
> typeof(e)
'number'

va quasi bene, non distingue gli errori; ma non disperare, ci sono isNan e isFinite:

> n = Number('x')
NaN
> isNaN(n)
true
> isNaN(42)
false
> i = 1 / 0
Infinity
> isFinite(i)
false
> isFinite(42)
true

OK 🤩 JavaScript (e Node) ha quel che mi serve; solo che è diverso da Python (e da tutti gli altri linguaggi di programmazione), dipende dal programmatore, un po come parlare olandese o, ancora più panicoso, finlandese; poi ci sarebbero il coreano e il vietnamese (ma ci sono bambini di due anni che li parlano); questioni d’ambiente. Come JavaScript.

JavaScript – stringhe e numeri – 1

E se invece di Python lo script lo faccio con Node.js? Continuo da qui.

OK, letta una riga come stringa come raccontato nel post precedente voglio ricavare i numeri che la compongono. In JavaScript c’è la funzione split(), quella che mi serve, verifico:

* conv $ node
> st = '1 2 3.1415 4 5'
'1 2 3.1415 4 5'
> st.split(' ')
[ '1', '2', '3.1415', '4', '5' ]

sembra OK ma

> st = ' 1   2\t   3.1415 \t\t   4  \t5 '
' 1   2\t   3.1415 \t\t   4  \t5 '
> console.log(st)
 1   2	   3.1415 		   4  	5
undefined
> st.split(' ')
[ '',
  '1',
  '',
  '',
  '2\t',
  '',
  '',
  '3.1415',
  '\t\t',
  '',
  '',
  '4',
  '',
  '\t5',
  '' ]
> st.split(' \t')
[ ' 1   2\t   3.1415', '\t   4 ', '5 ' ]
> st.split('\t')
[ ' 1   2', '   3.1415 ', '', '   4  ', '5 ' ]

no; crea stringhe vuote e non toglie i tabs (\t).

Quando il gioco si fa duro s’invocano le regexs 💥 😁

> st
' 1   2\t   3.1415 \t\t   4  \t5 '
> st.match(/[^ \t]+/g)
[ '1', '2', '3.1415', '4', '5' ]

OK! è lei 🤩

Ci sono tante funzioni che convertono la stringa in numero. Mi riferisco agli interi ma salvo casi particolari vale tutto anche per i floating.

> sti = '123'
'123'
> +sti
123
> 1 * sti
123
> sti / 1
123
> sti - 0
123

ma attenzione:

> sti + 0
'1230'
> 0 + sti
'0123'

e poi ci sono stringhe malfatte

> stierr = '123a'
'123a'
> stierr * 1
NaN

Ci sono parseInt() e parseFloat() come nel C (quasi):

> sti = '123'
'123'
> parseInt(sti)
123
> parseInt(sti, 16)
291
> parseInt(sti, 8)
83
> stierr = '123z'
'123z'
> parseInt(stierr)
123
> parseInt(stierr, 16)
291
> parseInt(stierr, 8)
83
> parseInt(stierr, 2)
1

Niente errore, interrompe la valutazione della stringa appena trova un carattere non rappresentante una cifra.

Le stringhe possono essere in formato scientifico, (non con parseInt())

> '12e3' * 1
12000
> '12e-3' * 1
0.012
> '12E3' * 1
12000
> '4.2e1'
'4.2e1'
> '4.2e1' * 1
42
> '4.2e-1' * 1
0.42
> parseInt('56e3')
56
> // errore qui ^
undefined
> parseFloat('5.6e3')
5600

Esiste anche la funzione generica Number() che accetta per gli interi il formato esadecimale ma non l’ottale (d’altronde chi lo usa ancora nel 2018?)

> Number('12')
12
> Number('12.5')
12.5
> Number('12.5z')
NaN
> Number('0x12.5')
NaN
> Number('0x12')
18
> Number('012')
12
> Number('12e3')
12000
> Number('12.3e2')
1230

Quindi? Per me è importante che gli errori vengano segnalati, quindi niente parseX(). Per base 10 mi sa che Number() è OK altrimenti (a malincuore) la moltiplicazione per uno o la sottrazione di zero.

Uh! ht @ E&G 🤩

E se invece JavaScript (Node.js) – 2

Seconda parte dell’esame di Node.js per valutarlo in alternativa a Python. Continuo da qui.

Ho scoperto che i giovani HTMLers la sanno molto più lunga di me. Ci sono anche corposi manuali cartacei che, come da prassi, non verranno mai letti, permettendomi di ricorrere al solito tormentone: RTFM!

C’è online tutto quello che serve, per esempio:

Probabilmente anche altre, ma credo mi basti.

Il codice minimo del post precedente può essere usato come template per gli scripts da costruire. Mancano solo due aggiustamenti (in realtà di più ma almeno questi): formattare i numeri e controllare la visibilità/vita delle variabili.

Per i numeri uso le funzioni round(), toFixed(), toExponential() e forse altre ancora.

Lo script del post precedente viene aggiornato così (file stuff_2.js):

#!/usr/bin/node

my_elab = function(st) {
  var v = Number(st);
  var s = Math.sin(v);
  var e = Math.exp(v);
  var bl = " "
  var r1 = v + bl + s.toFixed(4) + bl + e.toExponential(4);
  if (v > 0) {
    var r2 = bl + Math.log(v).toFixed(4);
  }else{
    var r2 = " -----";
  }
  return r1 + r2;
}

if (process.argv.length < 3) {
  console.log("nome-script file-dei-dati");
  process.exit(2);
}

var fdati = process.argv[2];
var fs = require("fs");
var dati = fs.readFileSync(fdati, "utf-8").split("\n");

var ndati = dati.length;
if (dati[ndati - 1] == "") { ndati -= 1};

var fout = fdati + "-js.out"
fs.writeFileSync(fout, dati[0] + "\n\n", 'utf8');

for (let n = 1; n < ndati; n ++) {
  fs.appendFileSync(fout, my_elab(dati[n]) + "\n", 'utf8');
}

Lo rendo eseguibile e lo lancio

* py-js-2 $ cat dati
Test minimo per confronto tra Python e Node.js
1
0
-1
42
* py-js-2 $ chmod +x stuff_2.js
* py-js-2 $ ./stuff_2.js dati
* py-js-2 $ cat dati-js.out
Test minimo per confronto tra Python e Node.js

1 0.8415 2.7183e+0 0.0000
0 0.0000 1.0000e+0 -----
-1 -0.8415 3.6788e-1 -----
42 -0.9165 1.7393e+18 3.7377

A me la cosa che colpisce è la selva di var. È che serve per rendere la variabile locale. Lo so che sono banale ma per me lo faccio lo stesso un esempio (file loc-glo.js):

#!/usr/bin/node

my_elab = function() {
  var n = 42;
  console.log('in my_elab\ng = ' + g + '   n = ' + n);
}

n = 0;
g = 8;
my_elab();
console.log('in main\ng = ' + g + '   n = ' + n);

eseguendolo ottengo

* py-js-2 $ node loc-glo.js
in my_elab
g = 8   n = 42
in main
g = 8   n = 0

Modifico lo script togliendo l’istruzione var (file no-var.js):

#!/usr/bin/node

my_elab = function() {
  n = 42;  //questa è l'istruzione modificata
  console.log('in my_elab\ng = ' + g + '   n = ' + n);
}

n = 0;
g = 8;
my_elab();
console.log('in main\ng = ' + g + '   n = ' + n);

e ottengo:

* py-js-2 $ node no-var.js
in my_elab
g = 8   n = 42
in main
g = 8   n = 42

Convertendo programmi “vecchi” capita che (come si usava) si trovano variabili globali, funzioni non pure o che non fanno una sola cosa; il problema non è specifico per Node/JavaScript ma questo linguaggio lo esalta. Alle volte, se si ha un progetto chiaro, è da valutare se non codificarlo ex-novo. Con Node? solo in casi particolari, p.es. deve girare sia nel Web che in locale; o il coder conosce bene (o solo) JS. Altrimenti per questioni di standardizzazione io sono per Python e Octave e Rust e Bash (ma non per Windows, meglio convertire in .py o .js) e altri (visto che non ho citato Racket?). Poi, certo, ci sono anche C++, Java e –forse– una spruzzata di Ruby e Perl. Ah! multipiattaforma, Linux e Windows. Anche se…

E se invece JavaScript (Node.js) – 1

Il mio linguaggio di default (che raccomando) è Python, versione 3.x. Non sempre, ci sono casi in cui è meglio ricorrere ad altri (già detto che Python è lento?) ma considerando che ha i moduli NumPy, matplotlib &co, almeno 2 REPL, GUIs (che non uso), una buona documentazione, è usatissimo da una comunità molto attiva, è facile (ahemmm) e conosciuto (ahemmm).

Ovvio che ci sono casi per cui possono tornare migliori Octave, C++, Rust, Racket, Java e altri ancora.

In ogni caso –per me– cosa si usa dev’essere open e disponibile su Linux e Windows (per mio cuggino, non per me).

Adesso mi viene chiesto “E JavaScript, Node.js in particolare, offline?”. Uh! ci devo pensare, forse un revival di “E Ruby” che una volta si sentiva spesso; a proposito cosa si dice di Ruby?

JavaScript è popolare, come Python, anzi in certi ambienti molto di più. E conosciuto, come e, forse, più di Python. Si può usare per riscrivere piccoli programmi obsoleti? Per esempio quelli vecchi in Visual Basic. Forse anche le macro di Excel (detesto la parola “macro” usata in questo senso). Uh! ci devo pensare, anzi provo, ne avrò per un paio di posts, questo è il primo.

In passato avevo esaminato l’ottimo Eloquent JavaScript di Marijn Haverbeke, adesso c’è la versione 3. Fondamentale per i nuovi.

Ma per me niente online, niente Web, piuttosto elaborazioni numeriche –vengo dal Fortran, me number cruncher.

Un test minimo, leggere un file di dati e eseguire su questi qualche calcolo. Sia dati il file dei dati:

Test minimo per confronto tra Python e Node.js
1
0
-1
42

La prima riga è una stringa, un titolo, le successive sono valori numerici di cui voglio calcolare il seno, l’esponenziale e il logaritmo (naturale). È un primo approccio, minimo, mancano controlli, ma lo voglio semplice. Salta fuori che per un niubbo come me i due scripts risultano molto simili:

Python

#!/usr/bin/python3
from sys import argv
from math import sin, exp, log

def my_elab(st):
  v = float(st)
  s = sin(v)
  e = exp(v)
  r1 = f'{st} {s} {e}'
  if v > 0:
    r2 = ' ' + str(log(v))
  else:
    r2 = ''
  return r1 + r2

if len(argv) < 2:
  print("file dei dati non indicato")
  exit(2)

fdati = argv[1]
with open(fdati, 'r') as f:
  dati = f.readlines()

fout = fdati + '-py.out'
g = open(fout, 'w')
print(dati[0], file = g)

ndati = len(dati)
for n in range(1, ndati):
  print(my_elab(dati[n].rstrip()), file = g)
* py-js-1 $ chmod +x stuff.py
* py-js-1 $ ./stuff.py dati
* py-js-1 $ cat dati-py.out
Test minimo per confronto tra Python e Node.js

1 0.8414709848078965 2.718281828459045 0.0
0 0.0 1.0
-1 -0.8414709848078965 0.36787944117144233
42 -0.9165215479156338 1.739274941520501e+18 3.7376696182833684

Node.js

#!/usr/bin/node

my_elab = function(st) {
  v = Number(st);
  s = Math.sin(v);
  e = Math.exp(v);
  bl = " "
  r1 = v + bl + s + bl + e;
  if (v > 0) {
    r2 = bl + Math.log(v);
  }else{
    r2 = "";
  }
  return r1 + r2;
}

if (process.argv.length < 3) {
  console.log("file dei dati non indicato");
  process.exit(2);
}

var fdati = process.argv[2];
var fs = require("fs");
var dati = fs.readFileSync(fdati, "utf-8").split("\n");

var ndati = dati.length;
if (dati[ndati - 1] == "") { ndati -= 1};

var fout = fdati + "-js.out"
fs.writeFileSync(fout, dati[0] + "\n\n", 'utf8');

for (let n = 1; n < ndati; n ++) {
  fs.appendFileSync(fout, my_elab(dati[n]) + "\n", 'utf8');
}
* py-js-1 $ chmod +x stuff.js
* py-js-1 $ ./stuff.js dati
* py-js-1 $ cat dati-js.out
Test minimo per confronto tra Python e Node.js

1 0.8414709848078965 2.718281828459045 0
0 0 1
-1 -0.8414709848078965 0.36787944117144233
42 -0.9165215479156338 1739274941520501000 3.7376696182833684

OK, sì, richiede affinamenti OK 😁

Il caso di case

Ogni tanto capita di dover aggiornare un programma vecchio, adeguarlo, ampliarlo. Capita anche che deve funzionare anche con Linux e allora il dilemma se modificarlo o rifarlo ex-novo non si pone.

Usiamo cosa? Python, ovvio (ahemmm… quasi ma questa volta sì). E salta fuori la solita storia che adesso provo ancora una volta a dire che secondo me non ha ragione d’essere.

Il programma vecchio, in Visual Basic, quindi solo Windows contiene (semplifico, riduco all’essenziale) questo codice:

Select Case N
  case 0 To 1
    Print "piccolo"
  Case 2
    Print "giusto"
  Case 3, 4
    Print "grosso"
  Case Else
    Print "valori tra 0 e 4"
End Select

Select/Case è la versione Basic di switch/case del C e linguaggi da esso derivati. Per esempio JavaScript:

n = parseInt(process.argv[2]);
switch(n) {
  case 0 : case 1:
    console.log("troppo piccolo");
    break;
  case 2:
    console.log("giusto!");
    break;
  case 3: case 4:
    console.log("troppo grande");
    break;
  default:
    console.log("valori validi da 0 a 4");
}
* case $ node case.js 0
troppo piccolo
* case $ node case.js 1
troppo piccolo
* case $ node case.js 2
giusto!
* case $ node case.js 4
troppo grande
* case $ node case.js 100
valori validi da 0 a 4
* case $ node case.js -100
valori validi da 0 a 4

Con Bash è anche più sinttetico, ma non gira su Windows (cioè non su tutti i ‘puters, non su quello del Boss).

N=$1
echo "caso" $N
case $N in
  0 | 1) echo "piccolo" ;;
  2) echo "giusto" ;;
  3 | 4) echo "grande" ;;
  *) echo "NO! valori [0 .. 4]" ;;
esac

Python non ha un’istruzione simile, perché non serve:

from sys import argv
n = int(argv[1])

if n in [0, 1]   : print("troppo piccolo")
elif n == 2      : print("giusto!")
elif n in [3, 4] : print("troppo grande")
else             : print("valori validi da 0 a 4")

if/elif/else. Secondo me è anche molto leggibile (OK, dipende dai gusti). Una soluzione simile è immediata con JavaScript:

n = parseInt(process.argv[2]);

if ([0, 1].indexOf(n) >= 0)      { console.log("troppo piccolo"); }
else if (n == 2)                 { console.log("giusto!"); }
else if ([3, 4].indexOf(n) >= 0) { console.log("troppo grande"); }
else                             { console.log("valori validi da 0 a 4"); }

Vero che è più bello della versione case?

Le graffe …, direbbe un C-ista o un programmatore Java e/o JS. Ma anche un pythonista potrebbe andare a capo dopo il :. E il codice vero sarebbe più complesso, su più righe. Certo; questo è solo un esempio per dire che **case non serve quasi mai.

Ma sai che il Lisp… OK, ma il Lisp è diverso.

Il Basic: OK, avevano ragione loro; e adesso…

Un post lungo quasi quarant’anni. Diciamo grosso modo 1979-2017. Sì perché a fine anno (non proprio alla fine, nel periodo delle feste) mi sono ritrovato con vecchi smanettoni (si dice ancora? allora almeno era il modo corrente per geek, hacker e affini) quasi tutti più giovani di me. Per via dell’età io allora già lavoravo e sul computer mica c’era tutta la scelta di adesso. I gestionali (avevano capito tutto, lì c’erano i $oldi) usavano Cobol e linguaggi di report (se ricordo bene si chiamavano così). Noi invece Fortran. Alcuni anche Assembler (si dovrebbe dire Assembly), che cambiava da macchina a macchina (non da marca a marca, IBM allora…).

Poi improvvisamente sono comparsi anche da noi i Personal Computer, Apple ][ e tutti gli altri. Costavano caro ma c’erano e si stavano diffondendo. E c’erano i componenti compatibili da assemblarsi (vero Giorgio e Giòrs? (sono 2 diversi, con una carriera molto diversa)).

Su quelle macchine stava per nascere il software del futuro: VisiCalc (il bisnonno di Excel) e WordStar (il bisnonno di Word). E già c’era un linguaggio nuovo di pakka, facile da usare, sexy da non dire, il Basic. Anzi, il Basic era il sistema operativo, non esattamente ma molto interconnesso. E stava tutto sullo stesso dischetto (la parola floppy sarebbe venuta dopo); anche perché c’era di solito un solo drive. OK, la sto facendo troppo lunga, sapete noi vecchietti, con un tubo in piòla… (per i non locali di qui: con un mezzo litro di vino all’osteria).

A me il Basic non piaceva. L’ho usato, dopo, ma senza mai sentirlo mio. E l’altra sera i giovani (di allora) che raccontavano… Hanno avuto ragione loro, alla lunga. Io sostenevo che i linguaggi seri (essenzialmente il Fortran) erano compilati, molto più performanti. Ma poi cos’è successo? Non so quando ma hanno vinto gli interpretati. Io me ne sono accorto (cioè no ma avrei dovuto) con il Turbo Pascal 3.0 (copia copiata da una scuola). Nel suo ambiente integrato, molto migliore di quello del GW-Basic si scriveva, testava, debuggava e poi, solo alla fine, si creava il .COM. E se le risorse richieste erano eccessive potevi fare gli overlays da concatenare.

Ma si continuava a usare il Basic, interpretato; cambiava nome ogni tanto ma era sempre quello, evolveva ma rimaneva quello.

Salto passi intermedi e arrivo a Python (posso dire tanto tempo fa ormai) e tutta la trafila di scrivere, compilare, linkare (quella che ancora m’era rimasta) svanì. Poi si sono aggiunti altri linguaggi, tanti, tutti interpretati. Tranne quelli di sistema (un paio abbondante, più qualcuno ancora in gestazione). Ma ha vinto la filosofia del Basic.

Dev’esserci qualche condizione ‘strologica perché qualcuno ha twittato il link a May 1, 1964: First Basic Program Runs.

Dice tante cose: per esempio che io, se avessi saputo, se fossi stato più aggiornato, le cose che dico adesso potevo dirle prima ancora di iscrivermi al Poli.

Bello che Kemeny and Kurtz formed a company in the 1980s to develop True BASIC, a lean version that meets ANSI and ISO standards.

Uh! si trova ancora, qui: True BASIC | The Original BASIC.
No, non c’è per Linux.

Però è nostalgia anche quella, diversa dalla mia ma nostalgia.

Invece il Basic di adesso (OK, da un po’ di tempo) è JavaScript, disponibile in tantissimi sapori. Dirò la Verità, tutta la Verità, solo la Verità, Vostro Onore: a me non è che davvero mi piace, anzi.

Sto rifacendo lo stesso errore, lo stessissimo. La vita le esperienze, proprio niente eh! Nada, zilch.

🤩

JavaScript 83 – conclusioni

Continuo da qui.

OK, finito l’esame dell’ottimo Eloquent LavaScript di Marijn Haverbeke 🚀 😊
Ottimo. Consigliatissimo. Il sottotitolo del libro è A modern introduction to programming. Vero ma io sono della vecchia scuola e JavaScript mi lascia un po’ perplesso come linguaggio, cosa nota e lollata, per esempio qui.

Ma sono solo io; il mondo la pensa diversamente. Nelle classifiche dei linguaggi che si pubblicano periodicamente JavaScript risulta sempre tra i primi. E io mi sono deciso a esaminarlo perché viene usato a Stanford.

La mia carenza, quella che m’impedisce di giudicarlo imparzialmente è che lo vedo come linguaggio tradizionale, da desktop. Cioè non lui ma Node (o equivalenti). Ignorando il Web e il suo ecosistema. Me l’hanno fatto notare tanti che non sanno che sono vecchio e ormai fuori dal mondo. Lo so che con CSS, HTML e qualcos’altro è quello che si usa adesso. OK, ci sarebbe anche il PHP (no, non voglio guardarlo). E tutti usano un’IDE come si deve. Probabilmente con Windows.

OK, magari qualcuno (l’amico PLG? ma dove lo trova il tempo?) potrebbe dire qualcosa, chissà… 😯

Un po’ di risorse le trovate sulla pagina Manuali e documenti di questo blog.
Node nella pagina citata è trattato separatamente (sono recidivo nel mio bias 😡).

Ah! un’ultimissima cosa di Node, per Linux. Riprendendo l’esempio di conversione dei fradi Fahrenheit in Celsius per renderlo eseguibile (OK, lo so, lo so ma repetita  juventus  juvant).

Dov’è Node:

Modifico lo script inserendoci la shebang, dev’essere la prima riga,

#!/usr/bin/node

if (process.argv.length > 2) {
  var F = process.argv[2];
  
  var C = (F - 32) * 5 / 9;
  C = C.toFixed(0);  
  console.log(F, "°F ->", C, "°C"); 
}

e lo rendo eseguibile

Per finire si può togliere l’estensione .js (conviene farne una copia, non si sa mai, metti che…) e per eseguirlo da una directory qualunque basta metterlo in una directory contenuta nella path, di solito ~/bin

:mrgreen:

JavaScript 82 – Node.js – 4

Continuo da qui, copio qui.

Streams
Una parola antica, non usata nel mondo Windows, ma vedremo che sono in pratica files.

We have seen two examples of writable streams in the HTTP examples—namely, the response object that the server could write to and the request object that was returned from http.request.

Writable streams are a widely used concept in Node interfaces. All writable streams have a write method, which can be passed a string or a Buffer object. Their end method closes the stream and, if given an argument, will also write out a piece of data before it does so. Both of these methods can also be given a callback as an additional argument, which they will call when the writing to or closing of the stream has finished.

It is possible to create a writable stream that points at a file with the fs.createWriteStream function. Then you can use the write method on the resulting object to write the file one piece at a time, rather than in one shot as with fs.writeFile.

Readable streams are a little more involved. Both the request variable that was passed to the HTTP server’s callback function and the response variable passed to the HTTP client are readable streams. (A server reads requests and then writes responses, whereas a client first writes a request and then reads a response.) Reading from a stream is done using event handlers, rather than methods.

Objects that emit events in Node have a method called on that is similar to the addEventListener method in the browser. You give it an event name and then a function, and it will register that function to be called whenever the given event occurs.

Readable streams have "data" and "end" events. The first is fired every time some data comes in, and the second is called whenever the stream is at its end. This model is most suited for “streaming” data, which can be immediately processed, even when the whole document isn’t available yet. A file can be read as a readable stream by using the fs.createReadStream function.

The following code creates a server that reads request bodies and streams them back to the client as all-uppercase text:

var http = require("http");
http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  request.on("data", function(chunk) {
    response.write(chunk.toString().toUpperCase());
  });
  request.on("end", function() {
    response.end();
  });
}).listen(8000);

The chunk variable passed to the data handler will be a binary Buffer, which we can convert to a string by calling toString on it, which will decode it using the default encoding (UTF-8).

The following piece of code, if run while the uppercasing server is running, will send a request to that server and write out the response it gets:

var http = require("http");
var request = http.request({
  hostname: "localhost",
  port: 8000,
  method: "POST"
}, function(response) {
  response.on("data", function(chunk) {
    process.stdout.write(chunk.toString());
  });
});
request.end("Hello server");

The example writes to process.stdout (the process’ standard output, as a writable stream) instead of using console.log. We can’t use console.log because it adds an extra newline character after each piece of text that it writes, which isn’t appropriate here.

Poi Marijn passa a costruire un semplice file server HTTP. Salto. Anche gli esercizi sono fuori dai miei interessi.

Il capitolo successivo è un progetto lungo, sul Web, salto anche questo.

Sono così giunto alla fine dell’esame elementare di JavaScript. Le concluioni? Prossimamente 😉

:mrgreen:

JavaScript 81 – Node.js – 3

Ian McRae

Continuo da qui, copio qui.

Il modulo del File System
One of the most commonly used built-in modules that comes with Node is the "fs" module, which stands for file system. This module provides functions for working with files and directories.

For example, there is a function called readFile, which reads a file and then calls a callback with the file’s contents (file rf.js):

var fs = require("fs");
fs.readFile("file.txt", "utf8", function(error, text) {
  if (error)
    throw error;
  console.log("The file contained:", text);
});

The second argument to readFile indicates the character encoding used to decode the file into a string. There are several ways in which text can be encoded to binary data, but most modern systems use UTF-8 to encode text, so unless you have reasons to believe another encoding is used, passing "utf8" when reading a text file is a safe bet. If you do not pass an encoding, Node will assume you are interested in the binary data and will give you a Buffer object instead of a string. This is an array-like object that contains numbers representing the bytes in the files (rfb.js).

var fs = require("fs");
fs.readFile("file.txt", function(error, buffer) {
  if (error)
    throw error;
  console.log("The file contained", buffer.length, "bytes.",
              "The first byte is:", buffer[0]);
});

A similar function, writeFile, is used to write a file to disk (wf.js).

var fs = require("fs");
fs.writeFile("graffiti.txt", "Node was here", function(err) {
  if (err)
    console.log("Failed to write file:", err);
  else
    console.log("File written.");
});

Here, it was not necessary to specify the encoding since writeFile will assume that if it is given a string to write, rather than a Buffer object, it should write it out as text using its default character encoding, which is UTF-8.

The "fs" module contains many other useful functions: readdir will return the files in a directory as an array of strings, stat will retrieve information about a file, rename will rename a file, unlink will remove one, and so on. See the documentation at nodejs.org for specifics (dir.js).

var fs = require("fs");

fs.readdir("./", function (err, files) {
  if (err) {
    throw err;
  }
  console.log(files);
});

Many of the functions in "fs" come in both synchronous and asynchronous variants. For example, there is a synchronous version of readFile called readFileSync( rfsync.js).

var fs = require("fs");
console.log(fs.readFileSync("file.txt", "utf8"));

Synchronous functions require less ceremony to use and can be useful in simple scripts, where the extra speed provided by asynchronous I/O is irrelevant. But note that while such a synchronous operation is being performed, your program will be stopped entirely. If it should be responding to the user or to other machines on the network, being stuck on synchronous I/O might produce annoying delays.

Il modulo HTTP
Another central module is called "http". It provides functionality for running HTTP servers and making HTTP requests.

This is all it takes to start a simple HTTP server (https.js):

If you run this script on your own machine, you can point your web browser at http://localhost:8000/hello to make a request to your server. It will respond with a small HTML page.

The function passed as an argument to createServer is called every time a client tries to connect to the server. The request and response variables are objects representing the incoming and outgoing data. The first contains information about the request, such as its url property, which tells us to what URL the request was made.

Marijn continua ma io salto, lascio la gestione Internet a chi ne sa più di me, o gli interessa di più 😜

:mrgreen:

JavaScript 80 – Node.js – 2

Continuo da qui, copio qui.

Moduli
Beyond the few variables I mentioned, such as console and process, Node puts little functionality in the global scope. If you want to access other built-in functionality, you have to ask the module system for it.

The CommonJS module system, based on the require function, was described in Chapter 10 [qui]. This system is built into Node and is used to load anything from built-in modules to downloaded libraries to files that are part of your own program.

When require is called, Node has to resolve the given string to an actual file to load. Pathnames that start with "/", "./", or "../" are resolved relative to the current module’s path, where "./" stands for the current directory, "../" for one directory up, and "/" for the root of the file system. So if you ask for "./world/world" from the file /home/marijn/elife/run.js, Node will try to load the file /home/marijn/elife/world/world.js. The .js extension may be omitted.

La realtà è più complessa e cambia in funzione del tipo di esecuzione (locale o Web) e del sistema operativo. Non ne parlo in questa serie di post introduttivi.

When a string that does not look like a relative or absolute path is given to require, it is assumed to refer to either a built-in module or a module installed in a node_modules directory. For example, require("fs") will give you Node’s built-in file system module, and require("elife") will try to load the library found in node_modules/elife/. A common way to install such libraries is by using NPM, which I will discuss in a moment. OOPS! ci dice (quasi tutto) Marijn 🚀

To illustrate the use of require, let’s set up a simple project consisting of two files. The first one is called main.js, which defines a script that can be called from the command line to garble a string.

var garble = require("./garble");

// Index 2 holds the first actual command-line argument
var argument = process.argv[2];

console.log(garble(argument));

The file garble.js defines a library for garbling strings, which can be used both by the command-line tool defined earlier and by other scripts that need direct access to a garbling function.

module.exports = function(string) {
  return string.split("").map(function(ch) {
    return String.fromCharCode(ch.charCodeAt(0) + 5);
  }).join("");
};

Remember that replacing module.exports, rather than adding properties to it, allows us to export a specific value from a module. In this case, we make the result of requiring our garble file the garbling function itself.

The function splits the string it is given into single characters by splitting on the empty string and then replaces each character with the character whose code is five points higher. Finally, it joins the result back into a string.

We can now call our tool like this:

Installare con NPM
NPM, which was briefly discussed in Chapter 10 [qui], is an online repository of JavaScript modules, many of which are specifically written for Node. When you install Node on your computer, you also get a program called npm, which provides a convenient interface to this repository.

For example, one module you will find on NPM is figlet, which can convert text into ASCII art—drawings made out of text characters. The following transcript shows how to install and use it:

OK, qualche warning e svela che uso una directory particolare (non conto di usare JavaScript oltre questi posts).

After running npm install, NPM will have created a directory called node_modules. Inside that directory will be a figlet directory, which contains the library. When we run node and call require("figlet"), this library is loaded, and we can call its text method to draw some big letters.

Somewhat unexpectedly perhaps, instead of simply returning the string that makes up the big letters, figlet.text takes a callback function that it passes its result to. It also passes the callback another argument, error, which will hold an error object when something goes wrong or null when everything is all right.

This is a common pattern in Node code. Rendering something with figlet requires the library to read a file that contains the letter shapes. Reading that file from disk is an asynchronous operation in Node, so figlet.text can’t immediately return its result. Asynchronicity is infectious, in a way—every function that calls an asynchronous function must itself become asynchronous.

There is much more to NPM than npm install. It reads package.json files, which contain JSON-encoded information about a program or library, such as which other libraries it depends on. Doing npm install in a directory that contains such a file will automatically install all dependencies, as well as their dependencies. The npm tool is also used to publish libraries to NPM’s online repository of packages so that other people can find, download, and use them.

This book won’t delve further into the details of NPM usage. Refer to npmjs.org for further documentation and for an easy way to search for libraries.

:mrgreen: