I have this piece of code:
(this code is inside another cycle, that is cycling 3 times)
...
text = "";
while((c = is.read())!=-1){
if(prev == '\r' && c == '\n'){
break;
}
text = text + (char) c;
prev = (char) c;
}
System.out.println(text);
...
is is InputStream, c is int, prev is char
With this code I build up a string from the InputStream. The reading should stop everytime when I get \r\n. Then it start's over again. Everything works fine except one thing. The stream I get looks like this:
1233\r\n544\r\nX
There is no delimeter after this input stream
With this I get the string 1233 from the first cycle and string 544 from the second cycle. But I won't get the last X, because the cycle won't stop there - and I don't know why. I thought that with is.read()=!-1 the cycle should stop when the stream ends. But it doesn't. The program is stuck inside that cycle.
Your question is unclear but here goes:
while( ( c = is.read() ) != -1 )
{
if(prev == '\r' && c == '\n')
{
break;
}
text = text + (char) c;
prev = (char) c;
}
Notice the order of execution. Check for \r\n and exit loop then append current character to text.
Do you see anything wrong with this logic?
Also you said
the cycle should stop when the stream ends. But it doesn't. The
program is stuck inside that cycle.
If the last two bytes are never \r\n or if the stream never closes it will never end and it will drop the last \n either way!
So which is it the loop never ends or the \n never gets appended?
If you want the loop to end at the end of the stream and at when a \r\n is detected you need to re-order your logic.
Garbage In Garbage Out:
Assuming that there are actually \r\n pairs in your InputStream. Are you sure they are there?, Step debugging would tell you for certain!
public static void main(final String[] args)
{
final InputStream is = System.in;
final StringBuilder sb = new StringBuilder(1024);
try
{
int i;
while ((i = is.read()) >= 0)
{
sb.append((char)i);
if (sb.substring(sb.length()-2).equals("\r\n"))
{
break;
}
}
}
catch (final IOException e)
{
throw new RuntimeException(e);
}
System.out.print(sb.toString());
}
You need to stop and learn how to use the step debugger that is in
your IDE. This would not be a question if you just stepped through
your code and put a few break points where things were not as you
wanted or expected them.
Related
I'm wondering how terminate a loop when the end of input is reached. I searched a lot for this but the only solutions I encounter envolve using Scanner which I'm not using. Instead, I am using the following function that reads each line of the input however I'm not quite understanding how can I end a loop that is constantly reading random numbers which means I can't simply put a clause on the while(clause) to reach the end of the loop.
CODE:
The loop that i'm talking about:
public static void main(String[] args) {
String str = "";
while (true){
str = readLn(200);
}
}
The method using for read lines:
static String readLn (int maxLg){ //utility function to read from stdin
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 (IOException e){
return (null);
}
if ((car < 0) && (lg == 0)) return (null); // eof
return (new String (lin, 0, lg));
}
If a stream is closed, the .read() call will return -1, which causes car to be less than 0, which causes the method to return; null if nothing is read yet, otherwise a string with the contents you got so far.
Even if you loop this method, it'll just keep returning null - once a stream starts returning -1 it will continue to do so.
Most likely, the stream is NOT, in fact, 'closed'. System.in doesn't close just because you wish it so, or because you stop typing. Maybe you'll type some more.
One easy way to 'close' system.in is to pipe a file into your process. something like:
echo "hello" | java YourApp
if you insist on keyboard input, you're looking at CTRL+Z or CTRL+D depending on OS in order to get standard in to be considered 'closed', and a little praying.
the while loop in your main should either break if str is null, or needs to be a do/while construct, which whiles as long as str != null, or just while on str != null, but then make sure to initialize str to a nonnull value or it won't even enter the while in the first place.
The while loop in your readLn method is already causing that loop to end if standard in is closed.
For your special case (there are more efficient ways to read a string from the stdin), the return value in readLn is either a string or null for eof.
So you can terminate the loop if the returned value is null:
while (true){
str = readLn(200);
if (str == null) {
break;
}
}
I am reading this example in my java book:
// Demonstrate unread().
import java.io.*;
class PushbackInputStreamDemo {
public static void main(String args[]) {
String s = "if (a == 4) a = 0;\n";
byte buf[] = s.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(buf);
int c;
try ( PushbackInputStream f = new PushbackInputStream(in) )
{
while ((c = f.read()) != -1) {
switch(c) {
case '=':
if ((c = f.read()) == '=')
System.out.print(".eq.");
else {
System.out.print("<-");
f.unread(c);
}
break;
default:
System.out.print((char) c);
break;
}
}
} catch(IOException e) {
System.out.println("I/O Error: " + e);
}
}
}
Here is the output for this example. Notice that == was replaced by ".eq." and = was replaced by "<-".
if (a .eq. 4) a <- 0;
When it reaches the single = sign, it reads it, prints out the <- and then unreads the = and puts it back on the pushbackInputStream right? So when it calls c = f.read(), it receives the = again right? so why aren't we in an infinite loop of <- symbols?
The while loop contains two possible branches: one when an = character is encountered, and one for everything else. For the everything-else scenario the character is simply dumped out to the console, so no infinite loop is created.
For the branch where we encounter an = character, it will then read the next character (after that = character) from the stream. If the next character is a second = character then .eq. is dumped out to the console, and nothing is pushed back onto the PushbackInputStream so the == sequence has been fully consumed and when we go around the while loop again it finds whatever comes after the ==. So this scenario causes no infinite loop.
If the next character after the first = is not a second = character then <- is dumped out to the console, and then that second character is pushed back onto the stream. Execution of the while loop then re-reads this character and (seeing as we already know it is not an = character) it will be dumped out to the console immediately after the <-. So this scenario does not cause an infinite loop.
The second do while loop in this code:
do {
ignore = (char) System.in.read();
} while(ignore != '\n');
I cannot comprehend what it actually does. All I understand is that when I take it out and run the program and guess an incorrect letter, program will print out 3 lines of the text "Guess letter now". Why does it do that 3 times without that code in place. What does that code even do? So I am confused and just cannot work it out.
public class GuessChar {
public static void main(String args[])
throws java.io.IOException {
char ch ,ignore, answer = 'k';
do {
System.out.println("Guess the letter now");
ch = (char) System.in.read();
do {
ignore = (char) System.in.read();
} while(ignore != '\n');
if(ch == answer) System.out.println("WELL DONE");
} while(ch != answer);
}
}
First look at the most confusing part:
char ignore;
do {
ignore = (char) System.in.read(); // (1)
} while (ignore != '\n'); // (2)
The do loop is entered unconditionally, so the line marked (1) is always executed. What it does is reading a single character from the standard input stream and assign the 16 least significant bits (thanks to the typecast) to the variable ignore. It is confusing letters in Java are actually of type int but as long as you are only dealing with simple-enough letters (eg symbols from the ASCII character set) they are the same.
The line marked (2) checks whether the just-read character is different from the newline character \n. If so, it will re-enter the loop and break it otherwise.
In combination, this discards any input up to and including the next newline character (ie the end of the current line).
Before that loop, you are also reading in a single character and store it away in the variable ch.
char ch = (char) System.in.read();
After the loop has discarded any remaining characters on the line, you test whether ch (that is, the first character that was on the line) is equal to answer and, if so, exit or otherwise start anew.
In conclusion, the program reads, line-by-line, input from the user (prompting for each line) until the user enters something that starts with the letter k.
Now we have clarified what the program does, let's see how we can improve it. It turns out that reading one character at a time is very inefficient and there are already methods in the standard library that will do a better job. Using them and more expressive variable names, the intent of the program becomes much clearer.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class GuessChar {
public static void main(String[] args) throws IOException {
char expected = 'k';
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Guess the letter now.");
while (true) {
String line = stdin.readLine();
if (line == null) { // end of input
System.out.println("Giving up, eh?");
break;
} else if (line.isEmpty()) {
System.out.println("Sorry, that was no input at all.");
} else if (line.charAt(0) == expected) {
System.out.println("Well done!");
break;
} else {
System.out.println("Sorry, please try again.");
}
}
}
}
'\n' is a character for "new line", meaning it waits for a user to press "Enter". So second loop will exit as soon as user press Enter, and will check for correct answer afterwards. If answer is wrong, all start from the beginning.
I am writing a program that is going to read a string from a file, and then remove anything that isn't 1-9 or A-Z or a-z. The A-Z values need to become lowercase. Everything seems to run fine, I have no errors, however my output is messed up. It seems to skip certain characters for no reason whatsoever. I've looked at it and tweaked it but nothing works. Can't figure out why it is randomly skipping certain characters because I believe my if statements are correct. Here is the code:
String dataIn;
int temp;
String newstring= "";
BufferedReader file = new BufferedReader(new FileReader("palDataIn.txt"));
while((dataIn=file.readLine())!=null)
{
newstring="";
for(int i=0;i<dataIn.length();i++)
{
temp=(int)dataIn.charAt(i);
if(temp>46&&temp<58)
{
newstring=newstring+dataIn.charAt(i);
}
if(temp>96&&temp<123)
{
newstring=newstring+dataIn.charAt(i);
}
if(temp>64&&temp<91)
{
newstring=newstring+Character.toLowerCase(dataIn.charAt(i));
}
i++;
}
System.out.println(newstring);
}
So to give you an example, the first string I read in is :
A sample line this is.
The output after my program runs through it is this:
asmlietis
So it is reading the A making it lowercase, skips the space like it is suppose to, reads the s in, but then for some reason skips the "a" and the "m" and goes to the "p".
You're incrementing i in the each of the blocks as well as in the main loop "header". Indeed, because you've got one i++; in an else statement for the last if statement, you're sometimes incrementing i twice during the loop.
Just get rid of all the i++; statements other than the one in the for statement declaration. For example:
newstring="";
for(int i=0;i<dataIn.length();i++)
{
temp=(int)dataIn.charAt(i);
if(temp>46&&temp<58)
{
newstring=newstring+dataIn.charAt(i);
}
if(temp>96&&temp<123)
{
newstring=newstring+dataIn.charAt(i);
}
if(temp>64&&temp<91)
{
newstring=newstring+Character.toLowerCase(dataIn.charAt(i));
}
}
I wouldn't stop editing there though. I'd also:
Use a char instead of an int as the local variable for the current character you're looking at
Use character literals for comparisons, to make it much clearer what's going on
Use a StringBuilder to build up the string
Declare the variable for the output string for the current line within the loop
Use if / else if to make it clear you're only expecting to go into one branch
Combine the two paths that both append the character as-is
Fix the condition for numbers (it's incorrect at the moment)
Use more whitespace for clarity
Specify a locale in toLower to avoid "the Turkey problem" with I
So:
String line;
while((line = file.readLine()) != null)
{
StringBuilder builder = new StringBuilder(line.length());
for (int i = 0; i < line.length(); i++) {
char current = line.charAt(i);
// Are you sure you want to trim 0?
if ((current >= '1' && current <= '9') ||
(current >= 'a' && current <= 'z')) {
builder.append(current);
} else if (current >= 'A' && current <= 'Z') {
builder.append(Character.toLowerCase(current, Locale.US));
}
}
System.out.println(builder);
}
While reading a file with a BufferedReader, I want it to skip blank lines and lines that start with '#'. Ultimately, each individual character is then added to an arraylist
inputStream = new BufferedReader(new FileReader(filename));
int c = 0;
while((c = inputStream.read()) != -1) {
char face = (char) c;
if (face == '#') {
//skip line (continue reading at first char of next line)
}
else {
faceList.add(face);
}
Unless I'm mistaken, BufferedReader skips blank lines automatically. Other than that, how would I go about doing this?
Would I just skip()? The length of the lines may vary, so I don't think that would work.
Do not attempt to read the file a character at a time.
Read in one complete line, into a String, on each iteration of your main loop. Next, check it it matches the specific patterns you want to ignore (empty, blanks only, starting with a #, etc). Once you have a line you want to process, only then iterate over the String a character at a time if you need to.
This makes checking for and ignoring blank lines and lines matching a pattern MUCH easier.
while((line=in.readline()) != null)
{
String temp = line.trim();
if (temp.isEmpty() || temp.startsWith("#"))
/* ignore line */;
else
...
}
Use continue. This will continue to the next item in any loop.
if (face == '#') {
continue;
}
else {
faceList.add(face);
}