开发者

Line number of the matched characters in JS/node.js

Is it p开发者_开发技巧ossible to find the line number of the regex matched characters for multiline inputs (such as files) in Javascript or node.js?


Yes, with a semi-awkward work around.

http://jsfiddle.net/tylermwashburn/rbbqn/

var string = "This\nstring\nhas\nmultiple\nlines.",
    astring = string.split('\n'),
    match = /has/, foundon;

Array.each(astring, function (line, number) {
    if (match.exec(line))
        foundon = number + 1;
});


how to get the line number by string index

function lineNumberByIndex(index,string){
    // RegExp
    var line = 0,
        match,
        re = /(^)[\S\s]/gm;
    while (match = re.exec(string)) {
        if(match.index > index)
            break;
        line++;
    }
    return line;
}

how that's useful

create a function that returns the line number for the first match

function lineNumber(needle,haystack){
    return lineNumberByIndex(haystack.indexOf(needle),haystack);
}

create a function that returns the line numbers for every match

function lineNumbers(needle,haystack){
    if(needle !== ""){
        var i = 0,a=[],index=-1;
        while((index=haystack.indexOf(needle, index+1)) != -1){
            a.push(lineNumberByIndex(index,haystack));
        }
        return a;
    }
}

Fiddle


Thank you @Shanimal I changed your code to also add the column position:

function lineNumberByIndex(index, string) {
  const re = /^[\S\s]/gm;
  let line = 0,
    match;
  let lastRowIndex = 0;
  while ((match = re.exec(string))) {
    if (match.index > index) break;
    lastRowIndex = match.index;
    line++;
  }
  return [Math.max(line - 1, 0), lastRowIndex];
}

const findOccurrences = (needle, haystack) => {
  let match;
  const result = [];
  while ((match = needle.exec(haystack))) {
    const pos = lineNumberByIndex(needle.lastIndex, haystack);
    result.push({
      match,
      lineNumber: pos[0],
      column: needle.lastIndex - pos[1] - match[0].length
    });
  }
  return result;
};

const text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.`;
findOccurrences(/dolor/gim, text).forEach(result =>
  console.log(
    `Found: ${result.match[0]} at ${result.lineNumber}:${result.column}`
  )
);


It may be a good idea to use a parser generator. Zach Carter's jison reports line numbers. Here's an example of a small utility that uses jison to parse JSON and report errors with line numbers. It may be a good starting point.

https://github.com/zaach/jsonlint

For a minimal solution, I might try getting the index from a regex using the exec method (/myregex/.exec(mystring).index), taking the substring (mystring.substring(0, index)), splitting it on newlines, and counting the number of elements in the array.


I would do it the opposite way

I think it will have better performance because of only one regexp

var text="aaaaaaaaaaaaaaaaaaaaa\naaaaaaaabaaaaaaaaaa\naaaaaaaaaaaaaaaaaaa";
var RE=/b/g
var until=text.split(RE)
if (until.length>1){//found
   var linenumber=until[0].split(/\n/g).length
}


The match object has both the match index and the original text, so you can efficiently just count the line breaks using only the match.

/**
 * Return the line number of the match, or -1 if there is no match.
 */
function matchLineNumber(m) {
  if (!m) {
    return -1
  }
  let line = 1
  for (let i = 0; i < m.index; i++) {
    if (m.input[i] == '\n') {
      line++;
    }
  }
  return line
}


const rx = /foo/
const ex1 = `foo`
const ex2 = `
hello
foo
`
const ex3 = `
hello
`

console.log(matchLineNumber(rx.exec(ex1)))
console.log(matchLineNumber(rx.exec(ex2)))
console.log(matchLineNumber(rx.exec(ex3)))


Updated from 2015 answer with more modern javascript and a code snippet.

const lineNumbers = (needle, haystack) => haystack
    .split(/^/gm)
    .map((v, i) => v.match(needle) ? i + 1 : 0)
    .filter(a => a);

Notes

  • one line number could have multiple matches
  • adds one to index to get actual line number
  • each match could also return an object with stats, line text, index of matches etc.

Try it

const input = `Pommy ipsum shortbread bent as a nine bob note bossy britches numbskull you 'avin a laugh tallywhacker, had a barney with the inlaws stiff upper lip jolly good they can sod off yorkshire pudding Victoria sponge cake knackered a tenner, houlligan a total jessie in a pickle 'tis I'm off to Bedfordshire oopsy-daisies. Fish fingers and custard ended up brown bread chap one would like knackered owt, a diamond geezer lass a reet bobbydazzler chips ey up duck half-inch it, bossy britches lass The Doctor gosh. Driving a mini The Hounds of Baskerville real ale Dr. Watson a right toff naff, wind up cockney off the hook.
At the boozer bossy britches got his end away teacakes, chuffed. Lost her marbles Sherlock doolally muck about shepherd's pie lost her marbles, toad in the whole bog off knee high to a grasshopper i'll be a monkey's uncle one would be honoured to. Have a butcher's at this wedding tackle balderdash off the hook bargain Betty utter shambles bovver boots collywobbles nuthouse throw a paddy bloody mary, made a pig's ear of it tip-top scrubber posh nosh muck about tally-ho bit of a div. Off the hook nuthouse bowler hat air one's dirty linen tally-ho gosh, in a pickle chaps ask your mother if a cuppa up North splendid, gallivanting around squirrel cornish pasty shepherd's pie.
Scrumpy scouser knackered fish fingers and custard twiglets a reet bobbydazzler scatterbrained we'll be 'avin less of that porky-pies tosser, willy scally biscuits nuthouse brilliant baffled in the goolies bloke. Macca a bottle of plonk ridicule apple and pears ask your mother if challenge you to a duel, slappers yorkshire pudding and thus well chuffed. What a mug slap-head we'll be 'avin less of that anorak loo, naff wedding tackle balderdash. God save the queen corgi bottled it one would be honoured to quid, her Majesty's pleasure a cracking nicked bent as a nine bob note, pigeons in Trafalgar Square scally accordingly.
A fiver lass pork scratchings scrote down the local and rambunctious ey up knee high to a grasshopper i'll be a monkey's uncle, by 'eck love fancy a cuppa numbskull what a load of guff a fiver on his bill lost the plot. Slappers fork out rather wellies pulled out the eating irons had a barney with the inlaws, jolly hockey sticks Queen Elizabeth blimey a fiver. Pigeons in Trafalgar Square unhand me sir bowler hat gobsmacked pikey scrubber odds and sods taking the mick spiffing, golly scouser jolly have a kip rambunctious guinness man and his whippet naff lad, mush chin up ended up brown bread good old fashioned knees up conked him one on the nose fancied a flutter Kate and Will.
Tallywhacker unhand me sir blighty absolute upper class stop arsing around quid, down the local chinwag off t'shop tip-top blighty. Geordie spiffing clock round the earhole on his bill ever so utter shambles manky a week on Sunday bow ties are cool, tip-top off with her head bobby 10 pence mix chippy devonshire cream tea bread and butter pudding, beefeater challenge you to a duel lost her marbles sling one's hook upper class fried toast on his tod. Lad wellies see a man about a dog oopsy-daisies supper copped a bollocking up North and we all like figgy pudding, ended up brown bread jolly good absolute naff on a stag do nose rag oopsy-daisies what a mug, willy sling one's hook come hither oo ecky thump hard cheese old boy doing my head in.`;

const lineNumbers = (needle, haystack) => haystack
    .split(/^/gm)
    .map((v, i) => v.match(needle) ? i + 1 : 0)
    .filter(a => a);

console.log(lineNumbers('honoured', input));

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜