Dosyadaki bir dizeyi nodejs ile değiştirme


167

MD5 dosya adları oluşturmak için md5 grunt görevini kullanın. Şimdi HTML dosyasındaki kaynakları görevin geri çağrısında yeni dosya adıyla yeniden adlandırmak istiyorum. Bunu yapmanın en kolay yolunun ne olduğunu merak ediyorum.


1
Keşke hem dosyaları yeniden adlandıracak ve bu dosyalar için herhangi bir başvuruyu değiştirecek / değiştirecek bir renamer ve dosya değiştirme kombinasyonu olsaydı.
Brain2000

Yanıtlar:


303

Basit normal ifadeyi kullanabilirsiniz:

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

Yani...

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

Tabii, ama ben metni değiştirmek ve sonra dosyayı tekrar yazmak zorunda okumak zorunda, ya da daha kolay bir yolu var, üzgünüm ben daha çok bir ön uç adamım.
Andreas Köberle

Belki bunu başarmak için bir düğüm modülü var, ama bunun farkında değilim. Tam bir örnek btw eklendi.
asgoth

4
@Zax: Teşekkürler, bu '
böcek'in

1
Bildiğim gibi üzgünüm utf-8 gibi birçok dili destek: Vietnamca, Çince ...
vuhung3990

Dize metninizde birden çok kez görünüyorsa, yalnızca bulduğu ilk dizenin yerini alır.
eltongonc

79

Replace benim için çalışmadığı için, bir veya daha fazla dosyadaki metni hızlı bir şekilde değiştirmek için basit bir npm paketi değiştirme dosyası oluşturdum. Kısmen @ asgoth'un cevabına dayanıyor.

Edit (3 Ekim 2016) : Paket şimdi vaatleri ve globları destekliyor ve kullanım talimatları bunu yansıtacak şekilde güncellendi.

Edit (16 Mart 2018) : Paket şu anda aylık 100.000'den fazla indirmeyi topladı ve CLI aracıyla birlikte ek özelliklerle genişletildi.

Yüklemek:

npm install replace-in-file

Modül gerektir

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

Değiştirme seçeneklerini belirtme

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

Vaatlerle asenkron değiştirme:

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

Geri arama ile eşzamansız değiştirme:

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

Senkron değiştirme:

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

3
Harika ve kullanımı kolay anahtar teslim modül. Async / await ve oldukça büyük bir klasör üzerinde bir glob ile kullanıldı ve şimşek
Matt Fletcher

Js

Ben inanıyorum, ama aynı zamanda daha büyük dosyalar için akış değiştirme uygulamak için devam eden bir çalışma var.
Adam Reis

1
güzel, bu SO cevabını okumadan önce bu paketi buldum ve (CLI aracı için) kullandım. love it
Russell Chisholm

38

Belki de "değiştir" modülü ( www.npmjs.org/package/replace ) sizin için de işe yarar . Dosyayı okumanız ve yazmanız gerekmez.

Belgelerden uyarlanmıştır:

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

Yollardaki dosya uzantısına göre nasıl filtre uygulayabileceğinizi biliyor musunuz? yollar gibi bir şey: ['path / to / your / file / *. js'] -> işe yaramıyor
Kalamarico

Glob kalıplarını bir yol dizisine genişletmek ve sonra bunların üzerinde yinelemek için düğüm glob'u kullanabilirsiniz.
RobW

3
Bu güzel, ama terk edildi. Hazır bir çözüm istiyorsanız, muhafaza edilen bir paket için stackoverflow.com/a/31040890/1825390 adresine bakın .
xavdid

1
Düğüm değiştirme adı verilen korunmuş bir sürümü de vardır ; ancak, kod tabanına bakıldığında, ne bu ne de dosya içinde yer değiştirme aslında dosyadaki metnin yerini almaz, kullanır readFile()ve writeFile()kabul edilen cevap gibi.
c1moore

26

ShellJS'nin bir parçası olan 'sed' işlevini de kullanabilirsiniz ...

 $ npm install [-g] shelljs


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

Daha fazla örnek için ShellJs.org adresini ziyaret edin .


Bu en temiz çözüm gibi görünüyor :)
Yerken

1
shxnpm komut dosyalarından çalışmanıza izin verir, ShellJs.org bunu tavsiye eder. github.com/shelljs/shx
Joshua Robinson

Bundan bende hoşlanıyorum. Bir oneliner, npm modülünden daha iyi, ancak surveral kod satırları ^^
suther

Üçüncü taraf bağımlılığını içe aktarmak en temiz çözüm değildir.
four43

Bu çok satırlı yapmaz.
chovy

5

Akışları kullanarak dosyayı okurken işleyebilirsiniz. Aynı tamponları kullanmak gibi ama daha kullanışlı bir API ile.

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
Ama ... ya yığın regexpFind dizesini ayırırsa? O zaman niyet başarısız olmaz mı?
Jaakko Karhu

Bu çok iyi bir nokta. bufferSizeDeğiştirdiğiniz dize daha uzun ayarlayarak ve son yığın kaydetme ve geçerli olan ile birleştirerek merak ediyorum eğer bu sorunu önlemek olabilir.
sanbor

1
Muhtemelen bu pasaj, değiştirilmiş dosya büyük bir değişken oluşturmak yerine doğrudan dosya sistemine yazılarak da geliştirilmelidir, çünkü dosya kullanılabilir bellekten daha büyük olabilir.
sanbor

1

Küçük bir yer tutucuyu büyük bir kod dizesiyle değiştirirken sorunla karşılaştım.

Yapıyordum:

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

Sorunun, JavaScript'in burada açıklanan özel değiştirme kalıpları olduğunu anladım . Değiştiren dize olarak kullandığım kod $içinde bazı vardı , bu çıktı kadar berbat oldu.

Benim çözümüm herhangi bir özel değiştirme YAPMAYAN işlev değiştirme seçeneğini kullanmaktı:

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

1

Atomik değiştirme için geçici yazma dosyası ile Node 7.6+ için ES2017 / 8.

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
}

Not, sadece ufacık dosyalar için hafızaya okunacak gibi.


Gerek yok bluebird, yerli kullanın Promiseve util.promisify .
Francisco Mateo

1
@FranciscoMateo Doğru, ancak 1 veya 2 fonksiyonun ötesinde söz veriyorum.
Matt

1

Linux veya Mac'te, tutmak basit ve sadece kabuk ile sed kullanın. Harici kütüphane gerekmez. Aşağıdaki kod Linux üzerinde çalışır.

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

Sed sözdizimi Mac'te biraz farklı. Şu anda test edemez, ama sadece "-i" sonra boş bir dize eklemeniz gerektiğine inanıyorum:

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

Finalden sonra "g"! " sed, bir satırdaki tüm örnekleri değiştirir. Çıkarın, her satırda yalnızca ilk oluşum değiştirilecektir.


1

@ Sanbor'un cevabını genişleterek, bunu yapmanın en etkili yolu orijinal dosyayı bir akış olarak okumak ve daha sonra her bir parçayı yeni bir dosyaya akıtmak ve son olarak orijinal dosyayı yeni dosyayla değiştirmek.

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

Bunun yerine bir dubleks akışı kullanırdım. burada belgelenmiş gibi nodejs doc dubleks akışları

Dönüştürme akışı, çıktının girişten bir şekilde hesaplandığı bir Dubleks akışıdır.


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

elbette akış olarak okumak için okuma şablonu işlevini geliştirebilir ve baytları daha verimli hale getirmek için satır satır oluşturabilirsiniz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.