I’m working on the UVa Online Judge problem set archive as a way to practice Java, and as a way to practice data structures and algorithms in general.
They give an example input file to submit to the online judge to use as a starting point (it’s the solution to problem 100).
Input from the standard input stream (java.lang.System.in) is required as part of any solution on this site, but I can’t understand the implementation of reading from System.in they give in their example solution. It’s true that the input file could consist of any variation of integers, strings, etc, but every solution program requires reading basic lines of text input from System.in, one line at a time. There has to be a better (simpler and more robust) method of gathering data from the standard input stream in Java than this:
public static String readLn(int maxLg) {
byte lin[] = new byte[maxLg];
int lg = 0, car = -1;
String line = “”;
try {
while (lg < maxLg) {
car = System.in.read();
if ((car < 0) || (car == ‘\n’)) {
break;
}
lin[lg++] += car;
}
} catch (java.io.IOException e) {
return (null);
}
if ((car < 0) && (lg == 0)) {
return (null); // eof
}
return (new String(lin, 0, lg));
}
I’m really surprised by this. It looks like something pulled directly from K&R’s “C Programming Language” (a great book regardless), minus the access level modifer and exception handling, etc. Even though I understand the implementation, it just seems like it was written by a C programmer and bypasses most of Java’s object oriented nature. Isn’t there a better way to do this, using the StringTokenizer class or maybe using the split method of String or the java.util.regex package instead?
You definitely don't have to read one byte at a time (you don't in C either, that's what fgets is for). Depending on what you're doing, you might use BufferedReader or Scanner:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
Scanner sc = new Scanner(System.in);
BufferedReader has a readLine method, while Scanner has a variety of useful methods, including nextLine, nextInt, nextDouble, etc. which handle conversions for you. It also has a regex-based delimiter for reading arbitrary tokens.
One thing to understand about Java is that it has a very clear distinction between binary data (Streams) and character data (Readers and Writers). There are default decoders and encoders (as used above), but you always have the flexibility to choose the encoding.
Related
So i'm trying to create a 2d character array from a .txt file. The first while-loop calculates to number of columns and rows. The second while-loop is to enter chars into the 2d array. However when i create BufferedReader br2 and use readLine() and then try to print it the line prints out "null". Why does the second BufferedReader start at the end of the file?
public Maze(FileReader reader){
try {
BufferedReader br = new BufferedReader(reader);
cols = 0;
rows = 0;
str = br.readLine();
while (str != null) {
if (str.length() > cols) {
cols = str.length();
}
rows++;
str = br.readLine();
}
}
catch (IOException e) {
System.out.println("Error");
}
maze = new char[getNumRows()][getNumColumns()];
try {
BufferedReader br2 = new BufferedReader(reader);
line = br2.readLine();
System.out.println(line);
while ((line = br2.readLine()) != null) {
System.out.println(line);
for (int i = 0; i < getNumColumns(); i++) {
maze[row][i] = line.charAt(i);
}
row++;
}
}
catch (IOException e) {
System.out.println("Error");
}
}
this is how I call it from main
public class RobotTest {
public static void main(String[] args) throws IOException{
File file = new File(args[0]);
Maze maze = new Maze(new FileReader(file));
}
}
You are using the same reader for initializing both of the BufferedReaders and after the first one finishes reading that means the next one will continue reading at the EOF. You must return the second one to the beginning of the file before iterating again through it.
You can reset the pointer of the reader by using FileReader.reset()
You can checkout mark() as well in the documentation.
Source: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Reader.html#reset()
As the name indicates, a 'BufferedReader' uses a buffer.
There's a reason for that.
Harddisks, network communications, SSDs - these are all concepts that tend to operate in terms of packets. They write or read largish chunks. For example, with networking, you can't just 'send a bunch of bytes down a wire' - you need to send a packet, because the packet includes information about where the packet is supposed to go and to solve the ordering issue (when you send packets on the internet, one you sent later may arrive earlier, so packets need an index number on them so the receiver can re-order them in the right way).
If you send one byte, okay - but that'll be ~200 bytes on the wire. Hence, sending 1 byte 5000 times is ~1 million bytes sent, whereas sending 5000 bytes in one go is only 5200 bytes; a 1000x difference!
Similar principles apply elsewhere, thus, 'send 1 byte' or 'read 1 byte' is often incredibly, factor 1000x inefficient.
Hence, buffers. You ASK for one character or one line (which can be quite a short line) from your BufferedReader and it will dutifully give you this, but under the hood it has read an entire largish chunk (because that is efficient), and will be fielding your further requests for e.g. another line from this buffer until it runs out and then it grabs another chunk.
The upshot of all that, is that you CAN NEVER use a reader ever again once you wrap it in a bufferedreader. You are 'committed' to the buffer now: That BufferedReader is the only thing you can read, from here on out, until the stream is done.
You're creating another one, and thus, your code is buggy: You're now effectively skpping whatever the first BufferedReader buffered; given that you're getting null out, that means right now it buffered the entire contents of the file, but perhaps on another system, a bigger file, it wouldn't return null, but some line deep into the file. Either way, you cannot use that filereader anymore once you have created a bufferedreader.
The solution is simple enough: Make the bufferedreader, once, and pass that around. Don't keep making BufferedReader instances out of it.
Also, resources need to be 'protected' - you must close them no matter how your code exits. If your code throws an error you need to still close the resources; failure to do so means your program will eventually get stuck and will be incapable of opening files, forever - the only way out is to completely close the app. Finally, FileReader is basically broken; it uses 'platform default charset encoding' which is anyone's guess. You want to 'hardcode' what encoding it has, and usually, the right answer is "UTF-8". This doesn't matter if the only characters are simple ASCII, but it's 2021. People use emojis, snowmen, and almost every language on the planet needs more than just a to z. If your encoding settings are off, it'll be mangled gobbledygook.
The newer Files API (java.io.File is outdated and you probably don't want to use it anymore) defaults to UTF-8, which is great, saves us some typing.
thus:
public static void main(String[] args) throws IOException {
try (var reader = Files.newBufferedReader(Paths.get(args[0]))) {
Maze maze = new Maze(reader);
}
}
I'm reading a book on Java, and we're on reading from a channel into a ByteBuffer. I found the way the author was structuring the while loop odd:
try (FileChannel inCh = (FileChannel) Files.newByteChannel(file)) {
ByteBuffer lengthBuf = ByteBuffer.allocate(8);
int strLength = 0;
ByteBuffer[] buffers = { null, ByteBuffer.allocate(8) };
while(true) {
if(inCh.read(lengthBuf) == -1)
break;
lengthBuf.flip();
strLength = (int)lengthBuf.getDouble();
buffers[0] = ByteBuffer.allocate(2*strLength);
if(inCh.read(buffers) == -1) {
System.err.println("EOF found reading ht eprime string.");
break;
}
System.out.printf("String length: %3s String: %-12s Binary Value: %3d%n", strLength,
((ByteBuffer) (buffers[0].flip())).asCharBuffer().toString(),
((ByteBuffer)buffers[1].flip()).getLong());
lengthBuf.clear();
buffers[1].clear();
}
System.out.println("\nEOF reached.");
} catch (IOException e) {
I tried it like this:
while(inCh.read(lengthBuf) != -1) {
and it works the same. Would there be a practical or code clarity reason the author would write it like he did?
It is clear that your version of the loop is semantically identical. However, that's not the only thing to consider.
Notice that further down the while loop there is a second condition that breaks out of the loop. I suspect that this is what has motivated the author to use while (true).
By writing it as while (true) you alert the reader to the fact that there must be one or more breaks inside the while. The reader is going to have to look inside the loop for breaks, and will hopefully find them both.
Written your way, the casual reader might scan the top of the code and assume that the while condition was the only way for the loop to terminate.
Another point to consider is that of symmetry, or balance. As written by the original author, the loop terminations are all of the same form. Namely breaks from within the loop. Your version feels asymmetrical. One termination point in the while test, and a further termination point, of a different nature, inside the loop.
The author has two exit points, one of which prints out an error before exiting the loop. Just makes the code a little more verbose in that case. It can be written in a number of different ways of course.
I'm practicing for a competitive tournament that will be in my faculty in a few weeks, and thus I encountered a small problem.
The competition restricted the use of java.io.* (except IOException...)
I need to read (from stdin) input, each test case is separated with a blank line. end of test cases - when EOF is found.
I need to find a way to get data from IO, without using java.io
so far, I got this (which works) - it returns a string containing each test case, and null when I'm out of test cases.
public static String getInput() throws IOException {
int curr=0;
int prev=0;
StringBuilder sb = new StringBuilder();
while (true) {
curr = System.in.read();
if (curr == -1) {
return null; //end of data
}
if (curr == '\r') {
curr = System.in.read();
}
if (curr == prev && curr == '\n') {
return sb.toString(); //end of test case
} //else:
sb = sb.append((char)curr);
prev = curr;
}
}
performance (for the IO) is neglected, so I don't care I read only one byte every time.
Question: Is there a more elegant (shorter and faster to code) way to achieve the same thing?
In fact, there are a few ways that you can process input in Java in competitive programming.
Approach 1: Using java.util.Scanner
This is the simplest way to read input, and it is also really straightforward to use. It can be slow if you have a huge amount of input. If your program keeps getting TLE (Time Limit Exceeded), but your program has the correct time complexity, try reading input with the second or third approach.
Initialization Scanner sc = new Scanner(System.in);
Reading an integer: int n = sc.nextInt();
Approach 2: Using java.io.BufferedReader
Use this one if there is a huge amount of input, and when the time limit of the problem is strict. It does require some more work, involving splitting the input by spaces, or using Integer.parseInt(str); to extract integers from the input.
You can find a speed comparison here https://www.cpe.ku.ac.th/~jim/java-io.html
Initialization: BufferedReader reader = new BufferedReader(System.in);
Reading an integer: int n = Integer.parseInt(reader.readLine());
Approach 3: Reading directly from FileDescriptor using custom reader
This approach is the fastest approach possible in Java. It does require a lot of work, including implementing the reader, as well as debugging should any problems arise. Use this approach if the time limit is strict and if you are allowed to bring code into the competition. This method is tested to be much faster than the second approach, but it would not usually provide you with an advantage since it is only about 2x the speed of the BufferedReader approach.
This is one implementation of such an approach written by my friend:
https://github.com/jackyliao123/contest-programming/blob/master/Utils/FastScanner.java
The usage of the reader really depends on your implementation of the reader. It is suggested to maintain one copy of the reader that is somewhat guaranteed to work, because the last thing you want in a contest is having a non-functional reader and debugging the rest of your program, thinking there are some bugs there.
Hope this helps and best wishes on your competition!
You could try the following and make it efficient by wrapping the System.in.
public static String readLine() throws IOException {
StringBuilder sb = new StringBuilder();
for (int ch; (ch = System.in.read()) > 0;)
if (ch == '\r') continue;
else if (ch == '\n') break;
else sb.append(ch);
return sb.toString();
}
EDIT: On Oracle JVM, System.in is a BufferedInputStream which wraps a FileInputStream which wraps a FileDescriptor. All these are in java.io.
You can try using the java.util.Scanner class if java.util is allowed. It has useful methods for reading in a line, a token or even a number as needed. But it is slower than BufferedReader and possibly slower than using System.in.read() directly.
Since System.in implements the InputStream interface, it might also be some speedup to use System.in.read(byte[] b) to read in the input. This way you can read in a bunch of bytes at a time instead of just the one, which should be faster. But the added complexity of having to code and debug it during the contest might not be worth it.
Edit:
Searching the web I found someone discussing using System.in.read(byte[] b) in the UVa forum back when UVa had terrible Java support.
You can use a scanner
import java.util.Scanner;//put this above the class
Scanner scanner = new Scanner(System.in); //this creates the scanner
int input = scanner.nextInt();
.nextInt() takes integers
.nextLine() takes strings
how can i write a query controlled loop that will continue to input int values from the user,adding each to the value sum,and then ask if the user has another value to input,until the user says that there are no more values
double sum = 0;
while (user.hasMoreInput()) {
double += user.mostRecentInput();
}
where you implement hasMoreInput and mostRecentInput to your likening.
This is how I write such a loop. I shouldn't be writing your homework for you, but I would nevertheless like to demonstrate my favorite style for this kind of loop.
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.print("> prompt ");
String str = in.readLine();
if (str == null) break;
process(str);
}
Some people may not like
while (true) - it looks like an infinite loop because it is! It's as infinite as the user's patience in typing input.
Single-line if - some people would prefer to make this a fully bracketed 3-liner. But I don't see any use in that; it doesn't become more readable as a result.
break in mid-loop. That's what break is for! It's your escape hatch from otherwise infinite loops.
If you're used to reading Java code, this is idiomatic and legible. Advantages:
It shows steps happening in exactly the sequence they happen;
It limits the scope of str to exactly where it's needed;
It's very explicit about the termination condition;
It's very concise. Fewer lines = fewer bugs, I always say.
There are a few pieces you need to handle. First, you need to know how to receive input from the user. The Java Developer's Almanac example (http://www.exampledepot.com/egs/java.io/ReadFromStdIn.html) that I found looks like this:
try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = "";
while (str != null) {
System.out.print("> prompt ");
str = in.readLine();
process(str);
}
} catch (IOException e) {
}
You might replace "> prompt" with something more descriptive. You'd also like to have a better way for the user to quit than entering a blank line, so maybe ask them to enter a 'q' if they are done. Then, change the comparison in the while loop to something like !str.toLowerCase().equals("q"). Then, you need to implement the process function to convert the string to an integer. Integer.parseInt(String) will return the integer value of a String that correctly represents an integer (ie, "3" or "49" but not "7e") and will throw a NumberFormatException otherwise. Because you don't want your application to fail with an exception on bad input, I think that process could just return 0 in the event of a non-Integer String (ie, when you catch a NumberFormatException).
Finally, you will want to have a sum variable initialized before your main loop, and you could add the result of process during each iteration. Then when the loop is over, you can print the result of process to the screen using System.out.println.
I purposely left out most of the code because this does sound like homework, but if you can understand all this enough to put it together I think you'll have learned it well enough to do it on your own.
This is how I typically do it. as little code as possible :).
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while((s = in.readLine()) != null)
process(s);
The Java Developer's Almanac is always a good source of basic examples such as yours.
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = "";
while (str != null) {
System.out.print("> prompt ");
str = in.readLine();
process(str);
}
Edit: Apparently some people think that a demonstration of the essentials of some technique should have error-checking. Perhaps I should also have commented the code, and provided a spec. Next time, I'll also develop some custom annotations to express pre and post conditions, or draft an implementation in Eiffel.
In trying to resolve Facebook's Puzzle "Hoppity Hop", http://www.facebook.com/careers/puzzles.php?puzzle_id=7, I'm reading one integer only from a file. I'm wondering if this is the most efficient mechanism to do this?
private static int readSoleInteger(String path) throws IOException {
BufferedReader buffer = null;
int integer = 0;
try {
String integerAsString = null;
buffer = new BufferedReader(new FileReader(path));
// Read the first line only.
integerAsString = buffer.readLine();
// Remove any surplus whitespace.
integerAsString = integerAsString.trim();
integer = Integer.parseInt(integerAsString);
} finally {
buffer.close();
}
return integer;
}
I have seen How do I create a Java string from the contents of a file?, but I don't know the efficiency of the idiom which answers that question.
Looking at my code, it seems like a lot of lines of code and Objects for a trivial problem...
The shortest method would be with a Scanner:
private static int readSoleInteger(String path) {
Scanner s = new Scanner(new File(path));
int ret = s.nextInt();
s.close();
return ret;
}
Note that Scanner swallows any IOExceptions, so that simplifies things a lot.
As for "most efficient"... well, the simple act of opening a file from the disk is likely to be the slowest part of any method you write for this. Don't worry too much about efficiency in this case.
Edit: I hadn't realized that the integer can have whitespace on either side of it. My code does not account for this currently, but it's easy to make the Scanner skip things. I've added the line
s.skip("\\s+");
to correct this.
Edit 2: Never mind, Scanner ignores whitespace when it's trying to parse numbers:
The strings that can be parsed as numbers by an instance of this class are specified in terms of the following regular-expression grammar:
(regexes snipped)
Whitespace is not significant in the above regular expressions.
I would use the Scanner class:
Scanner sc = new Scanner(new File("my_file"));
int some_int = sc.nextInt();