Uso lo md5 grunt task per generare nomi file MD5. Ora voglio rinominare le fonti nel file html con il nuovo nome file nel callback dell'attività. Mi chiedo quale sia il modo più semplice per farlo.Sostituire una stringa in un file con nodejs
risposta
È possibile utilizzare semplice espressione regolare:
var result = fileAsString.replace(/string to be replaced/g, 'replacement');
Quindi ...
var fs = require('fs')
fs.readFile(someFile, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
var result = data.replace(/string to be replaced/g, 'replacement');
fs.writeFile(someFile, result, 'utf8', function (err) {
if (err) return console.log(err);
});
});
È inoltre possibile utilizzare la funzione di 'sed' che fa parte di ShellJS ...
$ npm install [-g] shelljs
require('shelljs/global');
sed('-i', 'search_pattern', 'replace_pattern', file);
Visita ShellJs.org per ulteriori esempi.
Forse il modulo "sostituisci" (www.npmjs.org/package/replace) funzionerebbe anche per te. Non richiederebbe di leggere e quindi scrivere il file.
Adattato dalla documentazione:
// install:
npm install replace
// require:
var replace = require("replace");
// use:
replace({
regex: "string to be replaced",
replacement: "replacement string",
paths: ['path/to/your/file'],
recursive: true,
silent: true,
});
Sai come si può filtrare per estensione file nei percorsi? qualcosa di simile ai percorsi: ['percorso/a/tuo/file/*. js'] -> non funziona – Kalamarico
È possibile utilizzare il nodo-glob per espandere i modelli glob a un array di percorsi e quindi scorrere su di essi. – RobW
Questo è bello, ma è stato abbandonato. Vedi http://stackoverflow.com/a/31040890/1825390 per un pacchetto gestito se si desidera una soluzione pronta all'uso. – xavdid
Dal replace non funzionava per me, ho creato un semplice pacchetto NPM replace-in-file di sostituire rapidamente il testo in uno o più file. È parzialmente basato sulla risposta di @ asgoth.
Modifica (3 ottobre 2016): il pacchetto ora supporta promesse e glob e le istruzioni di utilizzo sono state aggiornate per riflettere questo.
Installare:
npm install replace-in-file
Richiede modulo
const replace = require('replace-in-file');
Specificare le opzioni di sostituzione
const options = {
//Single file
files: 'path/to/file',
//Multiple files
files: [
'path/to/file',
'path/to/other/file',
],
//Glob(s)
files: [
'path/to/files/*.html',
'another/**/*.path',
],
//Replacement to make (string or regex)
from: /Find me/g,
to: 'Replacement',
};
sostituzione asincrono con promesse:
replace(options)
.then(changedFiles => {
console.log('Modified files:', changedFiles.join(', '));
})
.catch(error => {
console.error('Error occurred:', error);
});
sostituzione asincrono con callback:
replace(options, (error, changedFiles) => {
if (error) {
return console.error('Error occurred:', error);
}
console.log('Modified files:', changedFiles.join(', '));
});
sostituzione sincrono:
try {
let changedFiles = replace.sync(options);
console.log('Modified files:', changedFiles.join(', '));
}
catch (error) {
console.error('Error occurred:', error);
}
Si potrebbe elaborare il file durante la lettura utilizzando i flussi. È come usare i buffer ma con un'API più conveniente.
var fs = require('fs');
function searchReplaceFile(regexpFind, replace, cssFileName) {
var file = fs.createReadStream(cssFileName, 'utf8');
var newCss = '';
file.on('data', function (chunk) {
newCss += chunk.toString().replace(regexpFind, replace);
});
file.on('end', function() {
fs.writeFile(cssFileName, newCss, function(err) {
if (err) {
return console.log(err);
} else {
console.log('Updated!');
}
});
});
searchReplaceFile(/foo/g, 'bar', 'file.txt');
Ma ... e se il pezzo divide la stringa regexpFind? L'intenzione non fallisce allora? –
Questo è un ottimo punto. Mi chiedo se impostando un 'bufferSize' più lungo della stringa che stai sostituendo e salvando l'ultimo blocco e concatenando con quello corrente potresti evitare questo problema. – sanbor
Probabilmente questo snippet dovrebbe anche essere migliorato scrivendo il file modificato direttamente nel filesystem piuttosto che creando una grande variabile in quanto il file potrebbe essere più grande della memoria disponibile. – sanbor
Vorrei utilizzare un flusso duplex invece. come documentato qui nodejs doc duplex streams
Un Transform flusso è un flusso Duplex in cui l'uscita è calcolato in qualche modo dall'ingresso.
Mi sono imbattuto in problemi quando si sostituiva un piccolo segnaposto con una grande stringa di codice.
che stavo facendo:
var replaced = original.replace('PLACEHOLDER', largeStringVar);
ho capito che il problema era modelli di ricambio appositamente di JavaScript, descritto here. Dal momento che il codice che stavo usando come stringa di sostituzione aveva un numero di $
in esso, stava rovinando l'output.
La mia soluzione era quella di utilizzare l'opzione di sostituzione funzione, che non fa alcuna sostituzione speciale:
var replaced = original.replace('PLACEHOLDER', function() {
return largeStringVar;
});
ES2017/8 per il nodo 7.6+ con un file di scrittura temporanea per la sostituzione atomica.
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs'))
async function replaceRegexInFile(file, search, replace){
let contents = await fs.readFileAsync(file, 'utf8')
let replaced_contents = contents.replace(search, replace)
let tmpfile = `${file}.jstmpreplace`
await fs.writeFileAsync(tmpfile, replaced_contents, 'utf8')
await fs.renameAsync(tmpfile, file)
return true
}
Nota, solo per file di dimensioni ridotte man mano che verranno letti in memoria.
Non c'è bisogno di 'bluebird', usa nativo' Promise' e [util.promisify] (https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original). –
@FranciscoMateo Vero, ma oltre 1 o 2 funzioni promisifyAll è ancora super utile. – Matt
Certo, ma devo leggere il file sostituire il testo e poi scrivere di nuovo il file, o c'è un modo più semplice, mi dispiace sono più un ragazzo di frontend. –
Forse c'è un modulo nodo per raggiungere questo obiettivo, ma non ne sono a conoscenza. Aggiunto un esempio completo btw. – asgoth
piccolo errore, sostituire alla riga 6 'someFile' con 'data' – Zax