Newline not matched in Regex while using JavaScript's ".replace" - node.js

I use node.js to read a file, save results to a variable, then replace a regex match with another string and write the result to the file.
The weirdest thing happened. I used Regex101 (with JS engine set and the same flags as my code) and it matched my desired text.
Link: https://regex101.com/r/WbmOLw/1
Implementing the exact same regex in my code, it fails to match!
I created the most minimal version representing my code:
tst.txt (the target file):
# Direct Methods
.method public constructor <init>()V
.locals 2
This seems to be the raw string (according to CyberChef):
# direct methods\n.method public constructor <init>()V\n .locals 2
test.js (the code):
var fs = require('fs');
var mainDir = 'tst.txt'
function start(){
fs.readFile(mainDir, "utf-8", function (err, data) {
data = data.replace(/(constructor \<init\>[(][)]V. \.locals )(\d+)/gms, 'BLABLAIDONTWORK')
console.log(data) // not changed
fs.writeFile(mainDir, data, 'utf8', function (err) {
if (err) return console.log(err);
})
});
}
start()
Whatever is written in the file isn't different at all. I suspect it's a strange side effect of newline handling, but can't figure out what's causing this! any help will be highly appreciated.

The file you've saved very likely has \r\n as a newline terminator, rather than just \n, so the V. pattern does not match. I can reproduce the problem on Windows, when the file was saved with Notepad++ with the \r\n setting enabled. Using \r\n in the pattern instead worked for me:
data = data.replace(/(constructor \<init\>[(][)]V\r\n \.locals )(\d+)/g, 'BLABLAIDONTWORK')
// ^^^^
To make the pattern more flexible, to match a newline on any platform, alternate with \n and (for rare cases) \r:
data = data.replace(/(constructor \<init\>[(][)]V(?:\r\n|\r|\n) \.locals )(\d+)/g, 'BLABLAIDONTWORK')
// ^^^^^^^^^^^^^^
(no need for the s modifier anymore, or the m modifier at all)

You can try using the following regex:
var fs = require('fs');
var mainDir = 'tst.txt'
function start(){
fs.readFile(mainDir, "utf-8", function (err, data) {
data = data.replace(/(constructor \<init\>[(][)]V\n*\s*\.locals\s)(\d+)/gms, 'BLABLAIDONTWORK')
console.log(data) // not changed
fs.writeFile(mainDir, data, 'utf8', function (err) {
if (err) return console.log(err);
})
});
}
start()

Related

Read Lines of figures in Text file and add String to each line

I'm trying to read lines of figures in a text file, add a string to each 'paid' and saved all inside another text file. But I'm confused with the output I'm getting. the commented codes are for saving it in a text file.
fs.readFile('./figures.txt', 'utf8', (err, data) => {
const figures = data.split('\n');
for (let i = 0; i < figures.length; i++) {
console.log(figures[i] + 'paid' );
}
// fs.writeFile('./paidApproved.txt', figures.join('\n'), (err) => {
// if (err) {
// reject(err);
// }
// });
});
Output i got
paid50
paid44
179987paid
Seems like the end-of-file marker is different from what you have considered in your code.
We have two end-of-file markers:
\n on POSIX
\r\n on Windows
So if you're sure about the structure of the file, and If you think the file itself has no problem, then you need to apply a tiny change in your code. Instead of explicitly putting \n as a line breaker, you need to ask os for the line breaker, and your code will be more flexible.
const os = require('os');
fs.readFile('./figures.txt', 'utf8', (err, data) => {
const figures = data.split(os.EOL);
// rest of code
});

return an array from glob node js

The issue
I'm using the answer here Get all files recursively in directories NodejS
however, when I assign it to a constant I'm trying to return the directories so I can have them available in an array, I have looked through globs documentation for an asnwer https://github.com/isaacs/node-glob, however I have had no successful results, I have tried using glob.end() and I have also console.log the folds variable below, I can see all the list of available methods and I have tried to use some of them with no success, does anyone know how to return the array like in the code example below? Thank you!
const glob = require('glob');
const src = 'assets';
function getFiles(err, res){
if (err) {
console.log('Error', err);
} else {
return res
}
}
let folds = glob(src + '/**/*', getFiles);
I had the same problem.
glob() is asynchronous and that can make returning the end result somewhat complicated.
Use glob.sync() instead (where .sync stands for synchronous)
Example:
const files = glob.sync(src + '/**/*');

how to use Node.JS foreach function with Event listerner

I am not sure where I am going wrong but I think that the event listener is getting invoked multiple times and parsing the files multiple times.
I have five files in the directory and they are getting parsed. However the pdf file with array 0 gets parsed once and the next one twice and third one three times.
I want the each file in the directory to be parsed once and create a text file by extracting the data from pdf.
The Idea is to parse the pdf get the content as text and convert the text in to json in a specific format.
To make it simple, the plan is to complete one task first then use the output from the below code to perform the next task.
Hope anyone can help and point out where i am going wrong and explain a bit about my mistake so i understand it. (new to the JS and Node)
Regards,
Jai
Using the module from here:
https://github.com/modesty/pdf2json
var fs = require('fs')
PDFParser = require('C:/Users/Administrator/node_modules/pdf2json/PDFParser')
var pdfParser = new PDFParser(this, 1)
fs.readdir('C:/Users/Administrator/Desktop/Project/Input/',function(err,pdffiles){
//console.log(pdffiles)
pdffiles.forEach(function(pdffile){
console.log(pdffile)
pdfParser.once("pdfParser_dataReady",function(){
fs.writeFile('C:/Users/Administrator/Desktop/Project/Jsonoutput/'+pdffile, pdfParser.getRawTextContent())
pdfParser.loadPDF('C:/Users/Administrator/Desktop/Project/Input/'+pdffile)
})
})
})
As mentioned in the comment, just contributing 'work-around' ideas for OP to temporary resolve this issue.
Assuming performance is not an issue then you should be able to asynchronously parse the pdf files in a sequential matter. That is, only parse the next file when the first one is done.
Unfortunately I have never used the npm module PDFParser before so it is really difficult for me to try the code below. Pardon me as it may require some minor tweaks to make it to work, syntactically they should be fine as they were written using an IDE.
Example:
var fs = require('fs');
PDFParser = require('C:/Users/Administrator/node_modules/pdf2json/PDFParser');
var parseFile = function(files, done) {
var pdfFile = files.pop();
if (pdfFile) {
var pdfParser = new PDFParser();
pdfParser.on("pdfParser_dataError", errData => { return done(errData); });
pdfParser.on("pdfParser_dataReady", pdfData => {
fs.writeFile("'C:/Users/Administrator/Desktop/Project/Jsonoutput/" + pdfFile, JSON.stringify(pdfData));
parseFile(files, done);
});
pdfParser.loadPDF('C:/Users/Administrator/Desktop/Project/Input/' + pdfFile);
}
else {
return done(null, "All pdf files parsed.")
}
};
fs.readdir('C:/Users/Administrator/Desktop/Project/Input/',function(err,pdffiles){
parseFile(pdffiles, (err, message) => {
if (err) { console.error(err.parseError); }
else { console.log(message); }
})
});
In the code above, I have isolated out the parsing logic into a separated function called parseFile. In this function it first checks to see if there are still files to process or not, if none then it invokes the callback function done otherwise it will do an array.pop operation to get the next file in queue and starts parsing it.
When parsing is done then it recursively call the parseFile function until the last file is parsed.

What is better async style in these two Node.js examples?

I am working through nodeschool.io learnyounode and on the fourth challenge, which is:
Write a program that uses a single asynchronous filesystem operation to read a file and print the number of newlines it contains to the console (stdout), similar to running cat file | wc -l.
I wrote one solution, which is different than the solution provided, but both seem to work, and I am curious to know which would be better style, and how they might function differently.
Here is my solution:
var fs = require('fs');
var fileAsArray = [];
function asyncRead(print) {
fs.readFile(process.argv[2], 'utf-8', function callback(error, contents) {
fileAsArray = contents.split('\n');
print();
})
}
function printNumberOfLines() {
console.log(fileAsArray.length - 1);
}
asyncRead(printNumberOfLines);
And here is the solution provided by learnyounode:
var fs = require('fs')
var file = process.argv[2]
fs.readFile(file, function (err, contents) {
// fs.readFile(file, 'utf8', callback) can also be used
var lines = contents.toString().split('\n').length - 1
console.log(lines)
})
I also noticed that the learnyounode code lacks semicolons. I thought they were strongly recommended/required?

Creating a file only if it doesn't exist in Node.js

We have a buffer we'd like to write to a file. If the file already exists, we need to increment an index on it, and try again. Is there a way to create a file only if it doesn't exist, or should I just stat files until I get an error to find one that doesn't exist already?
For example, I have files a_1.jpg and a_2.jpg. I'd like my method to try creating a_1.jpg and a_2.jpg, and fail, and finally successfully create a_3.jpg.
The ideal method would look something like this:
fs.writeFile(path, data, { overwrite: false }, function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
or like this:
fs.createWriteStream(path, { overwrite: false });
Does anything like this exist in node's fs library?
EDIT: My question isn't if there's a separate function that checks for existence. It's this: is there a way to create a file if it doesn't exist, in a single file system call?
As your intuition correctly guessed, the naive solution with a pair of exists / writeFile calls is wrong. Asynchronous code runs in unpredictable ways. And in given case it is
Is there a file a.txt? — No.
(File a.txt gets created by another program)
Write to a.txt if it's possible. — Okay.
But yes, we can do that in a single call. We're working with file system so it's a good idea to read developer manual on fs. And hey, here's an interesting part.
'w' - Open file for writing. The file is created (if it does not
exist) or truncated (if it exists).
'wx' - Like 'w' but fails if path exists.
So all we have to do is just add wx to the fs.open call. But hey, we don't like fopen-like IO. Let's read on fs.writeFile a bit more.
fs.readFile(filename[, options], callback)#
filename String
options Object
encoding String | Null default = null
flag String default = 'r'
callback Function
That options.flag looks promising. So we try
fs.writeFile(path, data, { flag: 'wx' }, function (err) {
if (err) throw err;
console.log("It's saved!");
});
And it works perfectly for a single write. I guess this code will fail in some more bizarre ways yet if you try to solve your task with it. You have an atomary "check for a_#.jpg existence, and write there if it's empty" operation, but all the other fs state is not locked, and a_1.jpg file may spontaneously disappear while you're already checking a_5.jpg. Most* file systems are no ACID databases, and the fact that you're able to do at least some atomic operations is miraculous. It's very likely that wx code won't work on some platform. So for the sake of your sanity, use database, finally.
Some more info for the suffering
Imagine we're writing something like memoize-fs that caches results of function calls to the file system to save us some network/cpu time. Could we open the file for reading if it exists, and for writing if it doesn't, all in the single call? Let's take a funny look on those flags. After a while of mental exercises we can see that a+ does what we want: if the file doesn't exist, it creates one and opens it both for reading and writing, and if the file exists it does so without clearing the file (as w+ would). But now we cannot use it neither in (smth)File, nor in create(Smth)Stream functions. And that seems like a missing feature.
So feel free to file it as a feature request (or even a bug) to Node.js github, as lack of atomic asynchronous file system API is a drawback of Node. Though don't expect changes any time soon.
Edit. I would like to link to articles by Linus and by Dan Luu on why exactly you don't want to do anything smart with your fs calls, because the claim was left mostly not based on anything.
What about using the a option?
According to the docs:
'a+' - Open file for reading and appending. The file is created if it does not exist.
It seems to work perfectly with createWriteStream
This method is no longer recommended. fs.exists is deprecated. See comments.
Here are some options:
1) Have 2 "fs" calls. The first one is the "fs.exists" call, and the second is "fs.write / read, etc"
//checks if the file exists.
//If it does, it just calls back.
//If it doesn't, then the file is created.
function checkForFile(fileName,callback)
{
fs.exists(fileName, function (exists) {
if(exists)
{
callback();
}else
{
fs.writeFile(fileName, {flag: 'wx'}, function (err, data)
{
callback();
})
}
});
}
function writeToFile()
{
checkForFile("file.dat",function()
{
//It is now safe to write/read to file.dat
fs.readFile("file.dat", function (err,data)
{
//do stuff
});
});
}
2) Or Create an empty file first:
--- Sync:
//If you want to force the file to be empty then you want to use the 'w' flag:
var fd = fs.openSync(filepath, 'w');
//That will truncate the file if it exists and create it if it doesn't.
//Wrap it in an fs.closeSync call if you don't need the file descriptor it returns.
fs.closeSync(fs.openSync(filepath, 'w'));
--- ASync:
var fs = require("fs");
fs.open(path, "wx", function (err, fd) {
// handle error
fs.close(fd, function (err) {
// handle error
});
});
3) Or use "touch": https://github.com/isaacs/node-touch
Todo this in a single system call you can use the fs-extra npm module.
After this the file will have been created as well as the directory it is to be placed in.
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFile(file, err => {
console.log(err) // => null
});
Another way is to use ensureFileSync which will do the same thing but synchronous.
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFileSync(file)
With async / await and Typescript I would do:
import * as fs from 'fs'
async function upsertFile(name: string) {
try {
// try to read file
await fs.promises.readFile(name)
} catch (error) {
// create empty file, because it wasn't found
await fs.promises.writeFile(name, '')
}
}
Here's a synchronous way of doing it:
try {
await fs.truncateSync(filepath, 0);
} catch (err) {
await fs.writeFileSync(filepath, "", { flag: "wx" });
}
If the file exists it will get truncated, otherwise it gets created if an error is raised.
This works for me.
// Use the file system fs promises
const {access} = require('fs/promises');
// File Exist returns true
// dont use exists which is no more!
const fexists =async (path)=> {
try {
await access(path);
return true;
} catch {
return false;
}
}
// Wrapper for your main program
async function mainapp(){
if( await fexists("./users.json")){
console.log("File is here");
} else {
console.log("File not here -so make one");
}
}
// run your program
mainapp();
Just keep eye on your async - awaits so everthing plays nice.
hope this helps.
You can do something like this:
function writeFile(i){
var i = i || 0;
var fileName = 'a_' + i + '.jpg';
fs.exists(fileName, function (exists) {
if(exists){
writeFile(++i);
} else {
fs.writeFile(fileName);
}
});
}

Resources