I get some result from an external command (semi-api) and want to parse the result. I'm only interested in the last few lines of the result.
How can get the last x lines of a string in Java?
I get some result from an external command (semi-api) and want to parse the result. I'm only interested in the last few lines of the result.
How can get the last x lines of a string in Java?
Here's a simple solution:
public static List<String> getLastLines(String string, int numLines) {
List<String> lines = Arrays.asList(string.split("\n"));
return new ArrayList<>(lines.subList(Math.max(0, lines.size() - numLines), lines.size()));
}
A solution that gives the result without parsing the whole string:
/**
* Created by alik on 3/31/17.
*/
public class Main {
// TODO: Support other EndOfLines, like "\r\n".
// One way is to just replace all "\r\n" with "\n" and then run the @getLastLines method.
public static List<String> getLastLines(String string, int numLines) {
List<String> lines = new ArrayList<>();
int currentEndOfLine = string.length();
if (string.endsWith("\n")) {
currentEndOfLine = currentEndOfLine - "\n".length();
}
for (int i = 0; i < numLines; ++i) {
int lastEndOfLine = currentEndOfLine;
currentEndOfLine = string.lastIndexOf("\n", lastEndOfLine - 1);
String lastLine = string.substring(currentEndOfLine + 1, lastEndOfLine);
lines.add(0, lastLine);
}
return lines;
}
@Test
public void test1() {
String text = "111\n" +
"222\n" +
"333\n" +
"444\n" +
"555\n" +
"666\n" +
"777\n";
List<String> lastLines = getLastLines(text, 4);
Assert.assertEquals("777", lastLines.get(lastLines.size() - 1));
Assert.assertEquals(4, lastLines.size());
}
@Test
public void test2() {
String text = "111\n" +
"222\n" +
"333\n" +
"444\n" +
"555\n" +
"666\n" +
"777";
List<String> lastLines = getLastLines(text, 4);
Assert.assertEquals("777", lastLines.get(lastLines.size() - 1));
Assert.assertEquals(4, lastLines.size());
}
}
Algorithm
lines
linesRequired
is less than size of lines
, ie, lineCount
lines
starting from lineCount - linesRequired
to lineCount
.Sample Implementation
private static final String SEPARATOR = "\n";
public static List<String> getLastLines(String string, int numLines) {
List<String> lines = Arrays.asList(string.split(SEPARATOR));
int lineCount = lines.size();
return lineCount > numLines ? lines.subList(lineCount - numLines, lineCount) : lines;
}
Here is another possible solution:
public class LastNLines {
public static List<String> getLastNLines(String inputString, int n) {
if(n < 0) {
return new ArrayList<>();
}
String[] tmp = inputString.split("(\\n|\\r)+");
if(n < tmp.length) {
return Arrays.asList(Arrays.copyOfRange(tmp, tmp.length - n, tmp.length));
}
return Arrays.asList(tmp);
}
public static void main(String[] args) {
String myExample =
" \n\r\r\n\nsome_text_1 " +
"\n" +
"some_text_2\n\n" +
"\n" +
" some_text_3\n\r " +
"\n" +
"some_text_4\n" +
"\n" +
"some_text_5\n\n\n";
List<String> result = LastNLines.getLastNLines(myExample, 2);
System.out.println(result.toString());
}
}
This one splits by multiple new lines
at once, so a text like this \n\n\n\n\n
will contain no Strings
after splitting and the result will be empty.
Java 8 Streams (memory friendly) answer.
Code:
public class Main
{
public static void main( String[] args )
{
String rawData = "John\n\nDavid\nGeorge\nFrank\nTom";
Pattern pattern = Pattern.compile("\\n");
System.out.println( lastN( pattern.splitAsStream( rawData ),4));
System.out.println( lastN( pattern.splitAsStream( rawData ),40));
}
public static <T> List<T> lastN( Stream<T> stream, int n )
{
Deque<T> result = new ArrayDeque<>( n );
stream.forEachOrdered( x -> {
if ( result.size() == n )
{
result.pop();
}
result.add( x );
} );
return new ArrayList<>( result );
}
}