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.

Andreas Köberle
sumber
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);
  });
});
Asgoth
sumber
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);
}
Adam Reis
sumber
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,
});
Slack Undertow
sumber
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.

Tony O'Hagan
sumber
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');
sanbor
sumber
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;
});
anderspitman
sumber
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.

Mat
sumber
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.

777
sumber
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);
  }
})()
sudo soul
sumber
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.

pungggi
sumber
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

Ezzabuzaid
sumber