I've found a number of custom Scanner classes on the internet for fast Java IO, but none of them offer a custom hasNext() method implementation, so I don't know how to read data when input is of variable size.
Here's an example of one of these classes:
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
/** call this method to initialize reader for InputStream */
static void init(InputStream input) {
reader = new BufferedReader(
new InputStreamReader(input) );
tokenizer = new StringTokenizer("");
}
/** get next word */
static String next() throws IOException {
while ( ! tokenizer.hasMoreTokens() ) {
//TODO add check for eof if necessary
tokenizer = new StringTokenizer(
reader.readLine() );
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt( next() );
}
static double nextDouble() throws IOException {
return Double.parseDouble( next() );
}
}
How could I go about writing this myself? Thanks.
Unfortunately the BufferedReader doesn't have a hasNext() function but it includes a ready() function which is a boolean function that tells if there is an input to be read or not, You can find about it here https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html#ready-- .
So if you want to added to the class you mentioned it will go like this :
static boolean ready() throws IOException {return reader.ready();}
However take care if the input is not present ready() will return false.
Related
i'm trying to read a FASTA file into a string in java.
My code works fine with small files, but when I choose a real FASTA file
which includes 5 million chars, so I can use this string, the program get stucked. get stucked= i see no output, and the program becomes with black screen.
public static String ReadFastaFile(File file) throws IOException{
String seq="";
try(Scanner scanner = new Scanner(new File(file.getPath()))) {
while ( scanner.hasNextLine() ) {
String line = scanner.nextLine();
seq+=line;
// process line here.
}
}
return seq;
}
Try to use a StringBuilder to process big loads of text data:
public static String ReadFastaFile( File file ) throws IOException {
StringBuilder seq = new StringBuilder();
try( Scanner scanner = new Scanner( file ) ) {
while ( scanner.hasNextLine() ) {
String line = scanner.nextLine();
seq.append( line );
// process line here.
}
}
return seq.toString();
}
I would try to use BufferedReader to read the file, something like this:
public static String readFastaFile(File file) throws IOException {
String seq="";
try(BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
// process line here.
}
}
return seq;
}
And also concatenate with StringBuilder like davidbuzatto said.
Sometimes i solve problems in 'codeforces.org' and after every solve i see the solution of others.But most of the others solution contains so many codes.
For example:
I have written a code for the problem Domino Piling like following.
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int m = in.nextInt(), n = in.nextInt();
int count = n*(m/2);
if(m%2 == 1)
count += n/2;
System.out.println(count);
in.close();
}
}
But petr who ranked in 2nd in codeforces wrote this solution like
import java.io.*;
import java.util.*;
public class Template implements Runnable {
private void solve() throws IOException {
int n = nextInt();
int m = nextInt();
writer.println(n * m / 2);
}
public static void main(String[] args) {
new Template().run();
}
BufferedReader reader;
StringTokenizer tokenizer;
PrintWriter writer;
public void run() {
try {
reader = new BufferedReader(new InputStreamReader(System.in));
tokenizer = null;
writer = new PrintWriter(System.out);
solve();
reader.close();
writer.close();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
int nextInt() throws IOException {
return Integer.parseInt(nextToken());
}
long nextLong() throws IOException {
return Long.parseLong(nextToken());
}
double nextDouble() throws IOException {
return Double.parseDouble(nextToken());
}
String nextToken() throws IOException {
while (tokenizer == null || !tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
}
Here what we see that he used thread and his own customized input/output technique.But i am not understanding why he solved this solution like this and what's the need of own customized i/o technique??
There's one thing which impressed me most is,besides his long code his code execution time is better than me.His code execution time is only '90 milliseconds' and mine is '248 milliseconds'.
Can anyone explain me the reason behind it??
Many coders actually have predefined templates for competitions. On codeforces, usually, they just copy that template into a file before even reading the problem they want to code and then code into that.
That bunch of code is actually fast input for Java. Some problems on codeforces require that. Petr must have just copied his usual template and coded that problem there, even if it wasn't necessary. The actual code Petr wrote specifically for this problem was the 3 lines function called Solve.
I saw this on a book called "算法竞赛入门经典" and the author said that this code is about 5~10 times faster than scanf.
Since I don't know java, I can't explain what this code means, but I'm sure you can understand it.
/** Class for buffered reading int and double values */
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
/** call this method to initialize reader for InputStream */
static void init(InputStream input) {
reader = new BufferedReader(
new InputStreamReader(input) );
tokenizer = new StringTokenizer("");
}
/** get next word */
static String next() throws IOException {
while ( ! tokenizer.hasMoreTokens() ) {
//TODO add check for eof if necessary
tokenizer = new StringTokenizer(
reader.readLine() );
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt( next() );
}
static double nextDouble() throws IOException {
return Double.parseDouble( next() );
}
}
I'm having a buffered reader streaming a file. There are two cases right now:
It is streaming a file generated on one PC, let's call it File1.
It is streaming a file generated on another Computer, let's call it File2.
I'm assuming my problem is caused by the EOLs.
BufferedReader does read both files, but for the File2, it reads an extra empty line for every new line.
Also, when I compare the line using line.equalsIgnoreCase("abc"), given that the line is "abc" it does not return true.
Use this code together with the two files provided in the two links to replicate the problem:
public class JavaApplication {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
File file = new File("C:/Users/User/Downloads/html (2).htm");
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
String line = "";
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}
File1,
File2
Note how the second file prints an empty line after each line...
I've been searching and trying and searching and trying, and couldn't come up with a solution.
Any ideas how to fix that? (Especially the compare thing?)
Works for me.
public class CRTest
{
static StringReader test = new StringReader( "Line 1\rLine 2\rLine 3\r" );
public static void main(String[] args) throws IOException {
BufferedReader buf = new BufferedReader( test );
for( String line = null; (line = buf.readLine()) != null; )
System.out.println( line );
}
}
Prints:
run:
Line 1
Line 2
Line 3
BUILD SUCCESSFUL (total time: 1 second)
As Joop said, I think you've mixed up which file isn't working. Please use the above skeleton to create an MCVE and show us exactly what file input isn't working for you.
Since you appear to have a file with reversed \r\n lines, here's my first attempt at a fix. Please test it, I haven't tried it yet. You need to wrap your InputStreamReader with this class, then wrap the BufferedReader on the outside like normal.
class CRFix extends Reader
{
private final Reader reader;
private boolean readNL = false;
public CRFix( Reader reader ) {
this.reader = reader;
}
#Override
public int read( char[] cbuf, int off, int len )
throws IOException
{
for( int i = off; i < off+len; i++ ) {
int c = reader.read();
if( c == -1 )
if( i == off ) return -1;
else return i-off-1;
if( c == '\r' && readNL ) {
readNL = false;
c = reader.read();
}
if( c == '\n' )
readNL = true;
else
readNL = false;
cbuf[i] = (char)c;
}
return len;
}
#Override
public void close()
throws IOException
{
reader.close();
}
}
Joop was right, after some more research it seems like, even though both files have specified a UTF-16 encoding in their header, one was encoded in UTF-16, and the other (File1) in UTF-8. This lead to the "double line effect".
Thanks for the effort that was put in answering this question.
I would like to restore an object from a CSV file. I need to know if scanner has 2 next values: scanner.hasNext()
the problem is my visit constructor takes 2 parameters and I need to ensure
there are at least 2 left in my csv file.
here is the relevant code:
/**
* method to restore a pet from a CSV file.
* #param fileName the file to be used as input.
* #throws FileNotFoundException if the input file cannot be located
* #throws IOException if there is a problem with the file
* #throws DataFormatException if the input string is malformed
*/
public void fromCSV(final String fileName)
throws FileNotFoundException, IOException, DataFormatException
{
FileReader inStream = new FileReader(fileName);
BufferedReader in = new BufferedReader(inStream);
String data = in.readLine();
Scanner scan = new Scanner(data);
scan.useDelimiter(",");
this.setOwner(scan.next());
this.setName(scan.next());
while (scan.hasNext()) {
Visit v = new Visit(scan.next(), scan.next());
this.remember(v);
}
inStream.close();
}
thanks in advance
hasNext() can also take a pattern, which provides quite a nice way to check this:
String pattern = ".*,.*";
while (scan.hasNext(pattern)) {
...
}
To address directly what I think you're asking: You can check scan.hasNext() inside the while loop.
public void fromCSV(final String fileName) throws FileNotFoundException, IOException, DataFormatException
{
FileReader inStream = new FileReader(fileName);
BufferedReader in = new BufferedReader(inStream);
String data = in.readLine();
Scanner scan = new Scanner(data);
scan.useDelimiter(",");
this.setOwner(scan.next());
this.setName(scan.next());
while (scan.hasNext()) {
String first = scan.next();
if(scan.hasNext()) {
String second = scan.next();
Visit v = new Visit(first, second);
this.remember(v);
}
}
inStream.close();
}
Although I think you're asking about the using scan.hasNext() in the while loop, you should also be checking before this.setOwner(scan.next()) and this.setName(scan.next()).
It might be better to take another approach to the problem as suggested by Hovercraft Full Of Eels in the comments. Better yet, since this is a CSV file, you can save yourself a lot of trouble by using a library such as Commons CSV or opencsv.
Updated Question (to be more clear):
Is there a way to design the InputStream below such that BufferedReader#readLine() will return after reading the new line character?
In the example below, readLine() hangs forever even though the reader has read a new line because (presumably) it is waiting for the buffer to fill up. Ideally, readLine() would return after reading the new line character.
I know something like what I want is possible, because when you read from System.in using BufferedReader#readLine(), it does not wait for the buffer to fill up before returning.
import java.io.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Example {
private static final class MyInputStream extends InputStream {
public final BlockingQueue<String> lines = new LinkedBlockingQueue<>();
private InputStream current = null;
#Override
public int read() throws IOException {
try {
if(current == null || current.available() == 0)
current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));
return current.read();
}
catch(InterruptedException ex) {
return -1;
}
}
}
public static void main(String[] args) throws Exception {
MyInputStream myin = new MyInputStream();
myin.lines.offer("a line\n");
BufferedReader in = new BufferedReader(new InputStreamReader(myin));
System.out.println(in.readLine());
}
}
Also, if there is a better way to send a string to an InputStream, I'm open to suggestions.
Accepted Solution:
Based on a suggestion from Sotirios Delimanolis in one of the comments on his solution, I'm just going to used a PipedInputStream instead. I've coupled it to a PipedOutputStream, and BufferedReader#readLine() returns immediately as long as I call PipedOutputStream#flush() after sending a string that contains a new line character.
After updated question, the only way to get the BufferedReader to stop reading after the new line character is to set the buffer size to 1, which completely removes the need for a BufferedReader.
You'll have to write your own implementation.
A BufferedReader reads more bytes than required. In your case, that means it will read further than the new line character. For example, with the Oracle JVM, it will attempt to read 8192 bytes. Through your inheritance hierarchy, this
System.out.println(in.readLine());
will attempt to invoke your read() method 8192 times.
The first 6 calls will return a value, one for each of the characters in your String's byte array. The next one, will see
if(current == null || current.available() == 0)
current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));
and current.available() will return 0 since the ByteArrayInputStream has been fully consumed. It will then attempt to take from the BlockingQueue and block indefinitely.
Also, if there is a better way to send a string to an InputStream, I'm open to suggestions.
Well, instead of an InputStream you can try a BufferedReader, with something that looks like this:
public int read(String directory) throws Exception{
String line = "";
File file = new File(directory);
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
do{
lines.add(br.readLine());
while(br.readLine() != null);
br.close();
return Integer.parseInt(line);
}