将文件中的字符串替换为nodejs

我使用Md5 grunt任务来生成MD5文件名。现在我想用任务回调中的新文件名重命名HTML文件中的源。我想知道最简单的方法是什么。

277206 次浏览

你可以使用简单的正则表达式:

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

所以…

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

你也可以使用'sed'函数,它是ShellJS的一部分…

 $ npm install [-g] shelljs




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

完整的文档…

也许“replace”模块(www.npmjs.org/package/replace)也适合你。它不需要您读取然后写入文件。

改编自文档:

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

由于replace对我不起作用,我创建了一个简单的npm包replace-in-file来快速替换一个或多个文件中的文本。这部分是基于@asgoth的回答。

编辑(2016年10月3日):这个包现在支持promise和glob,使用说明已经更新以反映这一点。

编辑(2018年3月16日):这个包已经积累了超过10万的每月下载量,现在已经扩展了额外的功能以及CLI工具。

安装:

npm install replace-in-file

需要的模块

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

指定替换选项

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

承诺的异步替换:

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

用回调进行异步替换:

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

同步替换:

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

您可以使用流在读取文件时处理该文件。这就像使用缓冲区,但使用了更方便的API。

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

我会使用双工流代替。像这里记录的Nodejs文档双工流

Transform流是一个双工流,输出是在其中计算的

我在用一串大代码替换一个小占位符时遇到了问题。

我正在做:

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

我发现问题是JavaScript的特殊替换模式,描述在这里。由于我使用的代码作为替换字符串有一些$在里面,它是混乱的输出。

我的解决方案是使用函数替换选项,它不做任何特殊替换:

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

ES2017/8 for Node 7.6+,带有用于原子替换的临时写文件。

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
}

注意,仅适用于较小的文件,因为它们将被读入内存。

在Linux或Mac上,keep很简单,只需使用sed和shell。不需要外部库。下面的代码可以在Linux上运行。

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

Mac上的sed语法有点不同。我现在不能测试它,但我相信你只需要在“-i”后面添加一个空字符串:

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

最后一个“!”后面的“g”使sed替换一行上的所有实例。删除它,将只替换每行中第一个出现的内容。

扩展@Sanbor的回答,最有效的方法是将原始文件作为流读取,然后也将每个块流到一个新文件中,然后最后用新文件替换原始文件。

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

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

当然,您可以改进读取模板函数,将其作为流读取,并按行组合字节,使其更有效

这可能会帮助到一些人:

这与全局替换略有不同

从终端运行
node replace.js < / p >

replace.js:

function processFile(inputFile, repString = "../") {
var fs = require('fs'),
readline = require('readline'),
instream = fs.createReadStream(inputFile),
outstream = new (require('stream'))(),
rl = readline.createInterface(instream, outstream);
formatted = '';


const regex = /<xsl:include href="([^"]*)" \/>$/gm;


rl.on('line', function (line) {
let url = '';
let m;
while ((m = regex.exec(line)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
        

url = m[1];
}


let re = new RegExp('^.* <xsl:include href="(.*?)" \/>.*$', 'gm');


formatted += line.replace(re, `\t<xsl:include href="${repString}${url}" />`);
formatted += "\n";
});


rl.on('close', function (line) {
fs.writeFile(inputFile, formatted, 'utf8', function (err) {
if (err) return console.log(err);
});


});
}




// path is relative to where your running the command from
processFile('build/some.xslt');

这就是它的作用。 我们有几个文件,xml:包括

然而,在开发过程中,我们需要向下移动的路径。

从这个

<xsl:include href="common/some.xslt" />

这个

<xsl:include href="../common/some.xslt" />
因此,我们最终运行两个regx模式,一个用于获取href,另一个用于写入 可能有一个更好的方法来做到这一点,但目前它是有效的

谢谢

如果有人想使用基于承诺的'fs'模块的任务。

const fs = require('fs').promises;


// Below statements must be wrapped inside the 'async' function:
const data = await fs.readFile(someFile, 'utf8');
const result = data.replace(/string to be replaced/g, 'replacement');
await fs.writeFile(someFile, result,'utf8');

通常,我使用tiny-replace-files替换文件或文件中的文本。这个pkg更小更轻… https://github.com/Rabbitzzc/tiny-replace-files < / p >

import { replaceStringInFilesSync } from 'tiny-replace-files'


const options = {
files: 'src/targets/index.js',
from: 'test-plugin',
to: 'self-name',
}


# await
const result = replaceStringInFilesSync(options)
console.info(result)