파일에서 문자열을 nodejs로 교체


167

내가 사용하는 MD5의 툴툴 거리는 소리 작업을 MD5 파일 이름을 생성 할 수 있습니다. 이제 작업 콜백에서 HTML 파일의 소스 이름을 새 파일 이름으로 바꾸고 싶습니다. 가장 쉬운 방법이 무엇인지 궁금합니다.


1
파일 이름을 바꾸고 해당 파일에 대한 모든 참조를 검색 / 바꾸는 이름 바꾸기 및 파일 내 바꾸기 조합이 있었으면 좋겠습니다.
Brain2000

답변:


303

간단한 정규식을 사용할 수 있습니다.

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

물론, 파일을 읽고 텍스트를 바꾼 다음 파일을 다시 작성해야합니까? 아니면 더 쉬운 방법이 있습니까? 죄송합니다.
Andreas Köberle

어쩌면 이것을 달성하기 위해 노드 모듈이있을 수도 있지만 잘 모르겠습니다. 전체 예제 btw를 추가했습니다.
asgoth

4
@Zax : 고마워요,이 버그가 너무 오래 살아남을 수 있다는 것에 놀랐습니다.)
asgoth

1
베트남어, 중국어 ... : 미안 같은 UTF-8 지원이 많은 언어를 알고
vuhung3990

텍스트에 문자열이 여러 번 나타나는 경우 찾은 첫 번째 문자열 만 바꿉니다.
eltongonc

79

replace가 작동하지 않았기 때문에 하나 이상의 파일에서 텍스트를 빠르게 바꾸는 간단한 npm 패키지 파일 바꾸기를 만들었습니다 . @asgoth의 답변을 부분적으로 기반으로합니다.

편집 (2016 년 10 월 3 일) : 패키지는 이제 약속 및 글로브를 지원하며 사용 지침이 업데이트되었습니다.

편집 (2018 년 3 월 16 일) :이 패키지는 현재 월간 다운로드 수가 100,000 회가 넘으며 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);
}

3
크고 사용하기 쉬운 턴키 모듈. async / await 및 꽤 큰 폴더에 대한 glob와 함께 사용했으며 번개가 빨랐습니다.
Matt Fletcher

노드 js의 문자열 제한이 256Mb 인 곳을 읽었
으므로

더 큰 파일에 대한 스트리밍 대체를 구현하는 작업도 진행 중입니다.
Adam Reis

1
이 SO 답변을 읽기 전에이 패키지 (CLI 도구 용)를 찾아서 사용했습니다. 그것을 사랑
러셀 Chisholm

38

아마도 "교체"모듈 ( 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,
});

경로에서 파일 확장자로 필터링하는 방법을 알고 있습니까? 경로와 같은 것 : [ 'path / to / your / file / *. js']-> 작동하지 않습니다
Kalamarico

node-glob를 사용하여 glob 패턴을 경로의 배열로 확장 한 다음 반복 할 수 있습니다.
RobW

3
이것은 좋지만 버려졌습니다. 기본 제공 솔루션을 원하는 경우 유지 관리 패키지에 대해서는 stackoverflow.com/a/31040890/1825390 을 참조하십시오 .
xavdid

1
node-replace 라는 유지 관리 버전도 있습니다 . 그러나 코드베이스를 살펴보면 파일 에서 텍스트 대체 또는 실제로 대체는 실제로 파일의 텍스트를 대체 하지 않으며 허용되는 답변 readFile()writeFile()마찬가지로 사용 됩니다.
c1moore

26

ShellJS의 일부인 'sed'기능을 사용할 수도 있습니다 ...

 $ npm install [-g] shelljs


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

더 많은 예제를 보려면 ShellJs.org 를 방문하십시오 .


이것은 가장 깨끗한 해결책 인 것 같습니다 :)
Yerken

1
shxnpm 스크립트에서 실행할 수 있도록 ShellJs.org에서 권장합니다. github.com/shelljs/shx
Joshua Robinson

나도 이것을 좋아한다. npm 모듈보다 oneliner가 더 좋지만 코드의 줄은 ^^
suther

타사 종속성을 가져 오는 것이 가장 깨끗한 솔루션은 아닙니다.
four43

이것은 여러 줄을 수행하지 않습니다.
chovy

5

스트림을 사용하여 읽는 동안 파일을 처리 할 수 ​​있습니다. 버퍼를 사용하는 것과 같지만 더 편리한 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');

3
그러나 ... 청크가 regexpFind 문자열을 분할하면 어떻게됩니까? 그때 의도가 실패하지 않습니까?
Jaakko Karhu

아주 좋은 지적입니다. bufferSize마지막 청크를 바꾸고 저장하고 현재의 청크와 연결하는 문자열보다 더 길게 설정하면 문제를 피할 수 있는지 궁금합니다 .
sanbor

1
파일이 사용 가능한 메모리보다 클 수 있으므로 큰 변수를 작성하지 않고 수정 된 파일을 파일 시스템에 직접 작성하여이 스 니펫을 개선해야 할 수도 있습니다.
sanbor

1

작은 자리 표시자를 큰 코드 문자열로 바꿀 때 문제가 발생했습니다.

나는하고 있었다:

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

문제가 여기 에 설명 된 JavaScript의 특수 대체 패턴이라는 것을 알았 습니다 . 교체 문자열로 사용했던 코드에 일부 코드 $가 포함되어 있으므로 출력이 엉망이되었습니다.

내 솔루션은 특수 교체를 수행하지 않는 함수 교체 옵션을 사용하는 것이 었습니다.

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

1

원자 교체를위한 임시 쓰기 파일이있는 Node 7.6+ 용 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
}

작은 파일은 메모리로 읽히므로 작습니다.


bluebirdnative Promiseutil.promisify를 사용할 필요가 없습니다 .
Francisco Mateo

1
@FranciscoMateo True이지만 1 또는 2 개 이상의 기능을 보장하는 PromisifyAll은 여전히 ​​매우 유용합니다.
Matt

1

Linux 또는 Mac에서 유지는 간단하며 쉘과 함께 sed를 사용하십시오. 외부 라이브러리가 필요하지 않습니다. 다음 코드는 Linux에서 작동합니다.

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

sed 구문은 Mac에서 약간 다릅니다. 지금은 테스트 할 수 없지만 "-i"뒤에 빈 문자열을 추가하면됩니다.

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

마지막 "!"이후의 "g" sed가 모든 인스턴스를 한 줄로 바꿉니다. 이를 제거하면 라인 당 첫 번째 항목 만 교체됩니다.


1

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


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

읽기 템플릿 기능을 개선하여 스트림으로 읽고 바이트 단위로 작성하여보다 효율적으로 만들 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.