pushback stream example in java textbook help. What is going on? - java

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.

Related

How can I prevent the user from entering the same letter in Hangman JAVA?

I am writing a hangman program and one of the requirements to a hangman game is preventing the user from entering the same letter twice.
I have written the code for that, but the problem is every time I enter a letter it says it is already entered. I need to only say it when it is entered the second time. You guys have any suggestions? I've been trying to fix this for the past few hours, I figured I could ask on here to find some help. I already looked at another Stackoverflow question regarding something similar to this but all those solutions have the same result.
In Java, how can I determine if a char array contains a particular character?
I've tried something like this but it won't work either:
boolean contains = false;
for (char c : store) {
if (c == character) {
System.out.println("Already Entered");
contains = true;
break;
}
}
if (contains) {
// loop to top
continue;
}
SECOND CLASS-
public void hangman(String word, int life) {
KeyboardReader reader = new KeyboardReader();
char[] letter = new char[word.length()];
char[] store = new char[word.length()];
String guess;
int i = 0, tries = 0, incorrect = 0, count = 1, v = 0;
while (i < word.length()) {
letter[i] = '-';
I would just use the String.contains() method:
String aString = "abc";
char aChar = 'a';
return aString.contains(aChar + "");
To keep track of guessed letters you can use a StringBuffer, appending them using a StringBuffer.append() to append new letters (maintaining state) and use the StringBuffer.toString() method to get the String representation when you need to do the comparison above.
Since Java 1.5 the class String contains the method contains(). My idea is to collect all entered letters into a string variable and using above method:
// My code line
String letterAlreadyEntered = "";
// Your code line
char character = reader.readLine().charAt(0);
// My code line
if (letterAlreadyEntered.contains("" + character) == true) {
//Put code here what ever you want to do with existing letter
} else {
letterAlreadyEntered += character;
}
In my opinion, this is an easier way to check for occurrences than in arrays, where you have to write your own check method.

How to terminate a loop that is reading an input?

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;
}
}

Reading from InputStream - stuck in cycle

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.

String not populating properly

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);
}

Retrieve method source code from class source code file

I have here a String that contains the source code of a class. Now i have another String that contains the full name of a method in this class. The method name is e.g.
public void (java.lang.String test)
Now I want to retieve the source code of this method from the string with the class' source code. How can I do that? With String#indexOf(methodName) i can find the start of the method source code, but how do i find the end?
====EDIT====
I used the count curly-braces approach:
internal void retrieveSourceCode()
{
int startPosition = parentClass.getSourceCode().IndexOf(this.getName());
if (startPosition != -1)
{
String subCode = parentClass.getSourceCode().Substring(startPosition, parentClass.getSourceCode().Length - startPosition);
for (int i = 0; i < subCode.Length; i++)
{
String c = subCode.Substring(0, i);
int open = c.Split('{').Count() - 1;
int close = c.Split('}').Count() - 1;
if (open == close && open != 0)
{
sourceCode = c;
break;
}
}
}
Console.WriteLine("SourceCode for " + this.getName() + "\n" + sourceCode);
}
This works more or less fine, However, if a method is defined without body, it fails. Any hints how to solve that?
Counting braces and stopping when the count decreases to 0 is indeed the way to go. Of course, you need to take into account braces that appear as literals and should thus not be counted, e.g. braces in comments and strings.
Overall this is kind of a thankless endeavour, comparable in complexity to say, building a command line parser if you want to get it working really reliably. If you know you can get away with it you could cut some corners and just count all the braces, although I do not recommend it.
Update:
Here's some sample code to do the brace counting. As I said, this is a thankless job and there are tons of details you have to get right (in essence, you 're writing a mini-lexer). It's in C#, as this is the closest to Java I can write code in with confidence.
The code below is not complete and probably not 100% correct (for example: verbatim strings in C# do not allow spaces between the # and the opening quote, but did I know that for a fact or just forgot about it?)
// sourceCode is a string containing all the source file's text
var sourceCode = "...";
// startIndex is the index of the char AFTER the opening brace
// for the method we are interested in
var methodStartIndex = 42;
var openBraces = 1;
var insideLiteralString = false;
var insideVerbatimString = false;
var insideBlockComment = false;
var lastChar = ' '; // White space is ignored by the C# parser,
// so a space is a good "neutral" character
for (var i = methodStartIndex; openBraces > 0; ++i) {
var ch = sourceCode[i];
switch (ch) {
case '{':
if (!insideBlockComment && !insideLiteralString && !insideVerbatimString) {
++openBraces;
}
break;
case '}':
if (!insideBlockComment && !insideLiteralString && !insideVerbatimString) {
--openBraces;
}
break;
case '"':
if (insideBlockComment) {
continue;
}
if (insideLiteralString) {
// "Step out" of the string if this is the closing quote
insideLiteralString = lastChar != '\';
}
else if (insideVerbatimString) {
// If this quote is part of a two-quote pair, do NOT step out
// (it means the string contains a literal quote)
// This can throw, but only for source files with syntax errors
// I 'm ignoring this possibility here...
var nextCh = sourceCode[i + 1];
if (nextCh == '"') {
++i; // skip that next quote
}
else {
insideVerbatimString = false;
}
}
else {
if (lastChar == '#') {
insideVerbatimString = true;
}
else {
insideLiteralString = true;
}
}
break;
case '/':
if (insideLiteralString || insideVerbatimString) {
continue;
}
// TODO: parse this
// It can start a line comment, if followed by /
// It can start a block comment, if followed by *
// It can end a block comment, if preceded by *
// Line comments are intended to be handled by just incrementing i
// until you see a CR and/or LF, hence no insideLineComment flag.
break;
}
lastChar = ch;
}
// From the values of methodStartIndex and i we can now do sourceCode.Substring and get the method source
Have a look at:- Parser for C#
It recommends using NRefactory to parse and tokenise source code, you should be able to use that to navigate your class source and pick out methods.
You will have to, probably, know the sequence of the methods listed in the code file. So that, you can look for the method closing scope } which may be right above start of next method.
So you code might look like:
nStartOfMethod = String.indexOf(methodName)
nStartOfNextMethod = String.indexOf(NextMethodName)
Look for .LastIndexOf(yourMethodTerminator /*probably a}*/,...) between a string of nStartOfMethod and nStartOfNextMethod
In this case, if you dont know the sequence of methods, you might end up skipping a method in between, to find an ending brace.

Categories

Resources