Ganti string dalam file dengan nodejs


166

Saya menggunakan tugas kasar MD5 untuk menghasilkan nama file MD5. Sekarang saya ingin mengganti nama sumber dalam file HTML dengan nama file baru dalam panggilan balik tugas. Saya bertanya-tanya apa cara termudah untuk melakukan ini.


1
Saya berharap ada kombinasi renamer dan replace-in-file, yang akan mengubah nama file, dan mencari / mengganti referensi untuk file-file itu juga.
Brain2000

Jawaban:


302

Anda bisa menggunakan regex sederhana:

var result = fileAsString.replace(/string to be replaced/g, 'replacement');

Begitu...

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);
  });
});

Tentu, tetapi apakah saya harus membaca file ganti teks dan kemudian menulis file lagi, atau apakah ada cara yang lebih mudah, maaf saya lebih dari seorang pria frontend.
Andreas Köberle

Mungkin ada modul simpul untuk mencapai ini, tapi saya tidak menyadarinya. Menambahkan contoh lengkap btw.
asgoth

4
@Zax: Terima kasih, saya terkejut 'bug' ini bisa bertahan begitu lama;)
asgoth

1
maaf saya tahu utf-8 mendukung banyak bahasa seperti: vietnamese, chinese ...
vuhung3990

Jika string Anda muncul beberapa kali dalam teks Anda, itu akan menggantikan hanya string pertama yang ditemukannya.
eltongonc

79

Karena ganti tidak berfungsi untuk saya, saya telah membuat paket pengganti npm file sederhana untuk mengganti teks dengan cepat dalam satu atau beberapa file. Sebagian didasarkan pada jawaban @ asgoth.

Sunting (3 Oktober 2016) : Paket sekarang mendukung janji dan gumpalan, dan instruksi penggunaan telah diperbarui untuk mencerminkan ini.

Edit (16 Maret 2018) : Paket ini telah mengumpulkan lebih dari 100 ribu unduhan bulanan sekarang dan telah diperpanjang dengan fitur-fitur tambahan serta alat CLI.

Install:

npm install replace-in-file

Membutuhkan modul

const replace = require('replace-in-file');

Tentukan opsi penggantian

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',
};

Penggantian asinkron dengan janji:

replace(options)
  .then(changedFiles => {
    console.log('Modified files:', changedFiles.join(', '));
  })
  .catch(error => {
    console.error('Error occurred:', error);
  });

Penggantian asinkron dengan panggilan balik:

replace(options, (error, changedFiles) => {
  if (error) {
    return console.error('Error occurred:', error);
  }
  console.log('Modified files:', changedFiles.join(', '));
});

Penggantian sinkron:

try {
  let changedFiles = replace.sync(options);
  console.log('Modified files:', changedFiles.join(', '));
}
catch (error) {
  console.error('Error occurred:', error);
}

3
Modul turn-key yang bagus dan mudah digunakan. Digunakan dengan async / menunggu dan gumpalan di atas folder yang cukup besar dan itu cepat kilat
Matt Fletcher

Apakah ini dapat bekerja dengan ukuran file lebih besar dari 256 Mb karena saya membaca suatu tempat bahwa batas string dalam node js adalah 256 Mb
Alien128

Saya percaya itu akan, tetapi ada juga pekerjaan yang sedang berjalan untuk mengimplementasikan penggantian streaming untuk file yang lebih besar.
Adam Reis

1
bagus, saya menemukan dan menggunakan paket ini (untuk alat CLI-nya) sebelum saya pernah membaca jawaban SO ini. menyukainya
Russell Chisholm

38

Mungkin modul "ganti" ( www.npmjs.org/package/replace ) juga akan bekerja untuk Anda. Anda tidak perlu membaca dan menulis file.

Diadaptasi dari dokumentasi:

// 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,
});

Apakah Anda tahu cara memfilter menurut ekstensi file di jalur? sesuatu seperti paths: ['path / ke / file / *. js'] -> tidak berfungsi
Kalamarico

Anda bisa menggunakan node-glob untuk memperluas pola glob ke array lintasan, dan kemudian beralih di atasnya.
RobW

3
Ini bagus, tetapi telah ditinggalkan. Lihat stackoverflow.com/a/31040890/1825390 untuk paket yang dikelola jika Anda menginginkan solusi out-of-the-box.
xavdid

1
Ada juga versi terpelihara yang disebut node-replace ; Namun, melihat pada basis kode tidak ini atau mengganti-in-file benar - benar mengganti teks dalam file, mereka menggunakan readFile()dan writeFile()seperti jawaban yang diterima.
c1moore

26

Anda juga dapat menggunakan fungsi 'sed' yang merupakan bagian dari ShellJS ...

 $ npm install [-g] shelljs


 require('shelljs/global');
 sed('-i', 'search_pattern', 'replace_pattern', file);

Kunjungi ShellJs.org untuk lebih banyak contoh.


ini tampaknya menjadi solusi terbersih :)
Yerken

1
shxmemungkinkan Anda menjalankan dari skrip npm, ShellJs.org merekomendasikannya. github.com/shelljs/shx
Joshua Robinson

Saya juga suka ini. Lebih baik oneliner, daripada npm-module, tetapi beberapa baris kode ^^
suther

Mengimpor ketergantungan pihak ketiga bukanlah solusi terbersih.
Four43

Ini tidak akan melakukan multilin.
chovy

5

Anda dapat memproses file saat sedang dibaca dengan menggunakan stream. Ini seperti menggunakan buffer tetapi dengan API yang lebih nyaman.

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');

3
Tapi ... bagaimana jika chunk membagi string regexpFind? Bukankah niatnya gagal?
Jaakko Karhu

Itu poin yang sangat bagus. Saya ingin tahu apakah dengan menetapkan bufferSizelebih lama dari string yang Anda ganti dan menyimpan potongan terakhir dan menyatukan dengan yang sekarang Anda dapat menghindari masalah itu.
sanbor

1
Mungkin potongan ini juga harus diperbaiki dengan menulis file yang dimodifikasi langsung ke sistem file daripada membuat variabel besar karena file mungkin lebih besar dari memori yang tersedia.
sanbor

1

Saya mengalami masalah ketika mengganti placeholder kecil dengan serangkaian kode besar.

Saya sedang melakukan:

var replaced = original.replace('PLACEHOLDER', largeStringVar);

Saya menemukan masalahnya adalah pola penggantian khusus JavaScript, dijelaskan di sini . Karena kode yang saya gunakan sebagai string pengganti punya beberapa$ di dalamnya, itu mengacaukan output.

Solusi saya adalah menggunakan opsi penggantian fungsi, yang TIDAK melakukan penggantian khusus:

var replaced = original.replace('PLACEHOLDER', function() {
    return largeStringVar;
});

1

ES2017 / 8 untuk Node 7.6+ dengan file tulis sementara untuk penggantian atom.

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
}

Catatan, hanya untuk file bertubuh kecil karena akan dibaca ke dalam memori.


Tidak perlu bluebird, gunakan asli Promisedan util.promisify .
Francisco Mateo

1
@FranciscoMateo Benar, tetapi melampaui 1 atau 2 fungsi, promisifyAll masih sangat berguna.
Mat

1

Di Linux atau Mac, tetap sederhana dan gunakan saja dengan shell. Tidak diperlukan perpustakaan eksternal. Kode berikut berfungsi di Linux.

const shell = require('child_process').execSync
shell(`sed -i "s!oldString!newString!g" ./yourFile.js`)

Sintaks sednya sedikit berbeda pada Mac. Saya tidak dapat mengujinya sekarang, tetapi saya yakin Anda hanya perlu menambahkan string kosong setelah "-i":

const shell = require('child_process').execSync
shell(`sed -i "" "s!oldString!newString!g" ./yourFile.js`)

"G" setelah final "!" membuat sed mengganti semua instance pada satu baris. Hapus itu, dan hanya kemunculan pertama per baris yang akan diganti.


1

Memperluas jawaban @ Sanbor, cara paling efisien untuk melakukan ini adalah dengan membaca file asli sebagai aliran, dan kemudian juga mengalirkan setiap potongan ke file baru, dan kemudian terakhir mengganti file asli dengan file baru.

async function findAndReplaceFile(regexFindPattern, replaceValue, originalFile) {
  const updatedFile = `${originalFile}.updated`;

  return new Promise((resolve, reject) => {
    const readStream = fs.createReadStream(originalFile, { encoding: 'utf8', autoClose: true });
    const writeStream = fs.createWriteStream(updatedFile, { encoding: 'utf8', autoClose: true });

    // For each chunk, do the find & replace, and write it to the new file stream
    readStream.on('data', (chunk) => {
      chunk = chunk.toString().replace(regexFindPattern, replaceValue);
      writeStream.write(chunk);
    });

    // Once we've finished reading the original file...
    readStream.on('end', () => {
      writeStream.end(); // emits 'finish' event, executes below statement
    });

    // Replace the original file with the updated file
    writeStream.on('finish', async () => {
      try {
        await _renameFile(originalFile, updatedFile);
        resolve();
      } catch (error) {
        reject(`Error: Error renaming ${originalFile} to ${updatedFile} => ${error.message}`);
      }
    });

    readStream.on('error', (error) => reject(`Error: Error reading ${originalFile} => ${error.message}`));
    writeStream.on('error', (error) => reject(`Error: Error writing to ${updatedFile} => ${error.message}`));
  });
}

async function _renameFile(oldPath, newPath) {
  return new Promise((resolve, reject) => {
    fs.rename(oldPath, newPath, (error) => {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  });
}

// Testing it...
(async () => {
  try {
    await findAndReplaceFile(/"some regex"/g, "someReplaceValue", "someFilePath");
  } catch(error) {
    console.log(error);
  }
})()

0

Saya akan menggunakan aliran duplex sebagai gantinya. seperti yang didokumentasikan di sini nodejs doc duplex stream

Transform stream adalah aliran Duplex di mana output dihitung dalam beberapa cara dari input.


0

<p>Please click in the following {{link}} to verify the account</p>


function renderHTML(templatePath: string, object) {
    const template = fileSystem.readFileSync(path.join(Application.staticDirectory, templatePath + '.html'), 'utf8');
    return template.match(/\{{(.*?)\}}/ig).reduce((acc, binding) => {
        const property = binding.substring(2, binding.length - 2);
        return `${acc}${template.replace(/\{{(.*?)\}}/, object[property])}`;
    }, '');
}
renderHTML(templateName, { link: 'SomeLink' })

pasti Anda dapat meningkatkan fungsi membaca template untuk dibaca sebagai aliran dan menyusun byte demi baris agar lebih efisien

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.