To begin with, your example is invalid--the character X
in your sample is found at positions (0,3,9,10)
, not (1,3,7,8)
. You're leaving the linefeed characters out of your reckoning, and you're starting the count at index 1 when you should start at zero.
The only way to relate absolute positions to line numbers is to map the positions of the line breaks for comparison. To do that on the fly, as others have said, is not difficult--just slow and tedious. If you're going to do multiple lookups, and you know the data won't change in between times, you should create a static map. You can use a List or a Map for that, but there's a class called SizeSequence that's ideal for the purpose. Check this out:
import javax.swing.SizeSequence;
public class Test
{
static SizeSequence createLineMap(String s)
{
String[] lines = s.split("(?<=\n)");
SizeSequence sseq = new SizeSequence(lines.length);
for (int i = 0; i < lines.length; i++)
{
sseq.setSize(i, lines[i].length());
}
return sseq;
}
public static void main(String[] args) throws Exception
{
String input = "X2\nX\n4\n56XX";
SizeSequence lineMap = createLineMap(input);
String target = "X";
int pos = -1;
while ((pos = input.indexOf("X", pos+1)) != -1)
{
System.out.printf("'%s' found in line %d (index %d)%n",
target, lineMap.getIndex(pos) + 1, pos);
}
}
}
output:
'X' found in line 1 (index 0)
'X' found in line 2 (index 3)
'X' found in line 4 (index 9)
'X' found in line 4 (index 10)
Notice how I split on the lookbehind (?<=\n)
instead of just \n
. That way I ensure that each line's character count includes the linefeed; all characters must be counted. (And on that note, I know there are issues with different line separators and surrogate pairs, but I'm leaving them out for clarity's sake.)
You could use the same technique on a file by substituting Scanner's findWithinHorizon()
method for split()
and 'indexOf()`.