Counting words in a string using methods - java

I have a project to make a program that takes a string as input and then prints the number of words in the string as output. We are supposed to use 3 methods in this, one to read input, one to print the output, and one to count the words.
I know I am missing something basic but I have spent hours on this and cannot figure out why the program wont run as it should. I need to keep the program pretty simple so I dont want to edit it too much, just find the issue and fix it so it will run correctly.
Example: Enter a string of text: The quick brown fox jumped over the lazy dog. 9 words
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter text: ");
String words = wordInput(in);
int count = wordCount(words);
System.out.println(words);
System.out.println(count);
printLine(count);
}
private static String wordInput(Scanner in)
{
String words = in.nextLine();
return words;
}
private static int wordCount(String words)
{
int length = words.length();
int ctr = 0;
int spot = 0;
int stop = 1;
char space = ' ';
char end = '.';
char com = ',';
char yes = '!';
char question = '?';
while (length > 0 && stop > 0)
{
if (words.charAt(spot) == space)
{
ctr++;
spot++;
}
else if (words.charAt(spot) == com)
{
spot++;
}
else if (words.charAt(spot) == yes || words.charAt(spot) == end || words.charAt(spot) == question)
{
stop = -1;
}
else if (spot > length)
{
stop = -1;
}
else
spot++;
}
return ctr + 1;
}
private static void printLine(int ctr)
{
System.out.println(ctr + " words");
}

Here is a rewritten wordCount that does as you request, minimal changes to the code. However, I am not sure it produces the answers you expect.
private static int wordCount(String words)
{
int length = words.length();
int ctr = 0;
int spot = 0;
char space = ' ';
char end = '.';
char com = ',';
char yes = '!';
char question = '?';
while (spot < length)
{
if (words.charAt(spot) == space)
{
ctr++;
spot++;
}
else if (words.charAt(spot) == com)
{
spot++;
}
else if (words.charAt(spot) == yes || words.charAt(spot) == end || words.charAt(spot) == question)
{
break;
}
else
spot++;
}
return ctr + 1;
}
However, fundamentally you are trying to count words in a string, and that is a known art. A few options and further reading:
http://www.quickprogrammingtips.com/java/find-number-of-words-in-a-string-in-java.html
Count words in a string method?
Which leads to a simpler result of:
private static int wordCount(String words) {
return words.split("\\s+").length;
}

Going on the assumption that whoever enters the string is not going to make a grammar error, try split the string at all spaces and set it equal to a string[]. Here is an example:
String temp = JOptionPane.showInputDialog(null, "Enter some text here");
String[] words = temp.split(" ");
JOptionPane.showMessage(null, String.format("Words used %d", words.length));

Related

Further modifications for o(n) complexity

How would I modify this for an O(n) time complexity? Basically, my program just finds the palindrome string and outputs a statement based on the findings.
class ChkPalindrome
{
public static void main(String args[])
{
String str, rev = "";
Scanner sc = new Scanner(System.in);
System.out.println("Enter a string:");
str = sc.nextLine();
int length = str.length();
for ( int i = length - 1; i >= 0; i-- )
rev = rev + str.charAt(i);
if (str.equals(rev))
System.out.println(str+" is a palindrome");
else
System.out.println(str+" is not a palindrome");
}
}
rev = rev + str.charAt(i) is the culprit. This statement by itself takes O(N) time, N being the size of rev.
Given that this line is inside a for loop that runs N times, your code is O(N^2) time.
The fix is the same fix for when you want to append to a string inside loops: Don't do that, use StringBuilder instead:
StringBuilder reverse = new StringBuilder();
for (int i = length - 1; i >= 0; i--) {
reverse.append(str.charAt(i));
}
if (str.contentEquals(rev)) {
// palindrome
} else {
// nope
}
Or, even faster (but by a constant factor with early exit, which both make a large difference in real life but don't affect O(N) numbers): Simply compare the first and last character, then the second-to-first and second-to-last, returning immediately with false if you see a mismatch. Also break up your methods (you should have a method that determines palindrome, and a separate method that then prints results). Something like:
boolean isPalindrome(String str) {
int len = str.length(), mid = len / 2;
for (int i = 0; i < mid; i++) {
char a = str.charAt(i);
char b = str.charAt(len - i);
if (a != b) return false;
}
return true;
}
// and then the code you have now simply becomes...
if (isPalindrome(str)) {
System.out.println(str + " is a palindrome");
} else {
System.out.println(str + " is not a palindrome");
}
Separate methods make them shorter, easier to read and understand, more re-usable, and easier to test.
StringBuilder reverse operation takes O(n) and String equals method takes O(n).Therefore below code will give O(n) complexity.
`public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("Enter a string:");
String s = in.nextLine();
String reverse = new StringBuilder(s).reverse().toString();
if(s.equals(reverse)){
System.out.println(s+" is a palindrome");
}
else {
System.out.println(s+" is not a palindrome");
}
}`
public static boolean isPalindrome(String str) {
for(int i = 0, j = str.length() - 1; i < j; i++, j--)
if(str.charAt(i) != str.charAt(j))
return false;
return true;
}

How to replace characters in a string in Java without using .replace?

The goal of this program is to prompt the user for a single character and a phrase, and then replace any instances of that character within that phrase with a '$'. My program below does just that, but when I showed it to my professor I was told that I cannot use .replace in the methods I built, so I have to figure out a way to not use that. I have worked at it for a while, and thus far I know that I can replace it with a for loop, but after several frustrating iterations, I can't seem to get it right. Excuse me if my code looks funky, I am still an introductory java student so I'm still learning the basics. I have provided a proposed solution at the end of my code snippet below.
public static char getKeyCharacter(String userInput) {
char keyCharacter;
Scanner inputStream = new Scanner(System.in);
while(userInput.length() > 1)
{
System.out.println("Please enter a SINGLE character to use as key: ");
userInput = inputStream.nextLine();
}
keyCharacter = userInput.charAt(0);
return keyCharacter;
}
public static String getString(String userResponse) {
Scanner inputStream = new Scanner(System.in);
String theString;
while(userResponse.length() > 500) {
System.out.println("Please enter a phrase or sentence >= 4 and <=500 characters: ");
userResponse = inputStream.nextLine();
}
while(userResponse.length() < 4) {
System.out.println("Please enter a phrase or sentence >= 4 and <=500 characters: ");
userResponse = inputStream.nextLine();
}
theString = userResponse;
return theString;
}
public static String maskCharacter(String theString, char keyCharacter){
String maskedString = "";
final char mask = '$';
maskedString = maskedString + theString.replace(keyCharacter, mask);
System.out.println("String with " + keyCharacter + " masked: ");
return maskedString;
}
public static String removeCharacter(String theString, char keyCharacter) {
String modifiedString = " ";
final char replaceChar = ' ';
modifiedString = modifiedString + theString.replace(keyCharacter, replaceChar);
System.out.println("String with " + keyCharacter + " removed:");
return modifiedString;
}
public static int countKey(String theString, char keyCharacter) {
int charCount = 0;
for (int c = 0; c < theString.length(); c++) {
if (theString.charAt(c) == keyCharacter) {
charCount++;
}
}
System.out.println("Occurences of " + keyCharacter + " in string:");
return charCount;
}
}
I believe the solution is will look something like this, but thus far I've been unsuccesful -
public static String maskCharacter(String theString, char keyCharacter){
String maskedString = "";
final char mask = '$';
for (int k = 0; k < theString.length(); k++) {
if (theString.charAt(k) == keyCharacter) {
keyCharacter = mask;
}
System.out.println("String with " + keyCharacter + " masked: ");
return maskedString;
}
My issue lies in making the maskedString = theString with all the keyCharacters replaced by mask. For the record, I have yet to learn anything about those fancy arrays, so if there is a way to do this using a simple for loop I would greatly appreciate it. Thank you for the assistance in advance!
I would use a StringBuilder and String#toCharArray() with a simple for-each loop. Like,
public static String maskCharacter(String theString, char keyCharacter){
StringBuilder sb = new StringBuilder();
for (char ch : theString.toCharArray()) {
if (ch == keyCharacter) {
sb.append('$'); // <-- mask keyCharacter(s).
} else {
sb.append(ch); // <-- it isn't the character to mask
}
}
return sb.toString();
}
I wouldn't use a StringBuilder: just use the result of toCharArray() directly:
char[] cs = theString.toCharArray();
for (int i = 0; i < cs.length; ++i) {
if (cs[i] == keyCharacter) cs[i] = '$';
}
return new String(cs);
Not only is it more concise, but:
It will run faster, because it's cheaper to access an array element than to invoke a method; and because it doesn't require StringBuilder's internal buffer to resize (although you could just pre-size that);
It will use less memory, because it doesn't require storage for the copy inside StringBuilder.
public static String maskCharacter(String theString, char keyCharacter){
String masked = "";
for (int i = 0 ; i < theString.length() ; i++) {
if (theString.charAt(i) == keyCharacter) {
masked += "$";
}
else {
masked+=theString.charAt(i)+"";
}
}
return masked;
}
An answer that only uses string concatenation and basic character access.
You seem to know that you can concatenate something to a string and get a different string.
maskedString = maskedString + ...;
You also know you can build a for-loop that gets each individual character using .charAt()
for (int k = 0; k < theString.length(); k++) {
char nch = theString.charAt(k);
}
You can check equality between chars
if (nch == keyCharacter)
... assuming you know about else-branches, isn't it clear you just need to put them together?
if (nch == keyCharacter) {
// append '$' to maskedString
}
else {
// append nch to maskedString
}
Of course this creates a new string on every loop iteration so it is not terribly efficient. But I don't think that's the point of the exercise.

String index out of range in jva

A string is a palindrome if it is spelled the same way backward and
forward.
Examples of palindromes include “Radar” and “Dammit, I’m mad!”.
Write a java program, PalindromeTester, that asks the user to enter a
word or sentence and then checks whether the entered string is a
palindrome or not.
Spaces, nonalphabetics (.,!:?-()\";), and case within the string have
to be ignored e.g., "Drab as a fool, aloof as a bard." is a
palindrome.
Your implementation should define and use the method isPalindrome to
test if a certain string is a palindrome. The signature of the
isPalindrome method is as follows:
boolean isPalindrome(String)
Following is a sample run of the program. The user’s input is shown in bold.
java PalindromeTester
Introduction to Computer Programming (CMPS 200)
Spring 2015-16 2 of 3
Enter a string: I love CMPS 200
The string "I love CMPS 200" is NOT a palindrome.
This is the code I made, it keeps giving me an error.
I would like to know what my error is and whether there's a faster easier way of writing this code
import java.util.Scanner;
public class PalindromeTester {
public static void main (String args []) {
Scanner console = new Scanner(System.in);
System.out.println("Enter a string: ");
String palindrome = console.next();
if (isPalindrome (palindrome)) {
System.out.print("The string \""+palindrome+" is a palindrome.");
} else {
System.out.print("The string \""+palindrome+" is NOT a palindrome.");
}
}
public static boolean isPalindrome (String palindrome) {
int constant = 1;
for (int i = 0 ; i <= (palindrome.length()-1) ; i++) {
for (int z= (palindrome.length()-1);i >= 0; i--) {
if (palindrome.charAt(i) <'#'||'Z'<palindrome.charAt(i)&&palindrome.charAt(i)<'`'||'['<palindrome.charAt(i)&&palindrome.charAt(i)<'{') {
i=i+1;
}
if (palindrome.charAt(z)<'#'||'Z'<palindrome.charAt(z)&&palindrome.charAt(z)<'`'||'['<palindrome.charAt(z)&&palindrome.charAt(z)<'{') {
z=z+1;
}
if (palindrome.charAt(i)==(palindrome.charAt(z))) {
constant = constant * 1;
} else {
constant = constant * 0;
}
}
}
if (constant == 0 ) {
return false;
} else {
return true;
}
}
}
One approach would be to strip the non alpha characters out of the string. Then check if the string is the same as itself reversed (while upper case):
public static boolean isPalindrome(String palindrome) {
StringBuilder sanitisedString = new StringBuilder();
for(char c : palindrome.toCharArray()) {
if(Character.isLetter(c)) {
sanitisedString.append(c);
}
}
return sanitisedString.toString().toUpperCase().equals(sanitisedString.reverse().toString().toUpperCase());
}
Index out of range is caused by palindrome.charAt(z)) after z = z + 1;
Keep simple :
public static boolean isPalindrome(String palindrome)
{
palindrome = palindrome.replaceAll("\\W", ""); // remove all non word character
palindrome = palindrome.toLowerCase();
int size = palindrome.length();
int halfSize = size / 2;
for (int i = 0; i < halfSize; i++)
{
if(palindrome.charAt(i) != palindrome.charAt(size - i - 1))
return false;
}
return true;
}
Why not just make a new String and save the reversed source(String) in it.
public static boolean readstring(String s)
{
String b = "";
for (int i= s.length() -1; i >=0 ;i--)
{
b = b + s.charAt(i);
}
System.out.print(b +" and "+ s +" ");
return b == s || b.Equals(s);
}
EDIT: Hopefully this meets the requirements, by the way dont use the word "ignore" but "allow"
public boolean isPalindrome(String word) {
int backward = word.length() - 1;
for (int x = 0; x < word.length(); x++) {
if (word.charAt(x)!= word.charAt(backward--)) {
return false;
}
}
return true;
}

Character count in Java

consider this code:
package q2b;
public class Q2b {
public static void main(String[] args) {
String kalimat = new String("Sue sells sea shells on the seashore!");
String upperLetter = "";
String removeLetter = "";
//Code to count the spacing in the string
for (int i = 0; i < kalimat.length(); i++) {
if (kalimat.charAt(i) == 's') {
upperLetter += 'S';
} else {
upperLetter += kalimat.charAt(i);
}
}
System.out.println("The letter modification in the string are :" + upperLetter);
}
}
My Question:
what code should I type to get how many lowercase letter 's' has been replace?
Add this:
int count = 0; // <-- here
for (int i = 0; i < kalimat.length(); i++) {
if (kalimat.charAt(i) == 's') {
upperLetter += 'S';
count++; // <-- and here
} else {
upperLetter += kalimat.charAt(i);
}
}
In the end you will have the number of substitutions in the variable count.
Declare and initialize an int variable to 0 outside the processing loop. Increment it while processing whenever you replace a s with an S.
As a side note, it is obviously out of the scope of this question, but using the + operator for repetitive String concatenation is highly inefficient. You should use a StringBuilder instead.
Based on my understanding about your question, the below code may help you,
Declared a variable 'count' and it will increase in the presence of each lowercase letter 's' in the iteration.
public class Q2b {
public static void main(String[] args) {
String kalimat = new String("Sue sells sea shells on the seashore!");
String upperLetter = "";
String removeLetter = "";
int count=0;
//Code to count the spacing in the string
for (int i = 0; i < kalimat.length(); i++) {
if (kalimat.charAt(i) == 's') {
upperLetter += 'S';
count++;
} else {
upperLetter += kalimat.charAt(i);
}
}
System.out.println("The letter modification in the string are :" + upperLetter);
System.out.println("Number of lower case letter 's' is :" + count);
}
}

Java program that does simple string manipulation is not working correctly

I wrote this program for school and it almost works, but there is one problem. The goal of the program is to take an inputted string and create a new string out of each word in the input beginning with a vowel.
Example:
input: It is a hot and humid day.
output: Itisaand.
Here is the driver:
public class Driver {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
System.out.print("Input: ");
String input = console.nextLine();
Class strings = new Class(input);
int beg=0;
for(int j=0;j<input.length();j++)
{
if(strings.isVowel(j)&&(j==0||input.charAt(j-1)==' '))
beg=j;
else if(strings.endWord(j)&&(beg==0||input.charAt(beg-1)==' '))
{
strings.findWord(beg, j);
}
}
System.out.print("Output: ");
strings.printAnswer();
}
}
And here is the class:
public class Class {
String input="",answer="";
public Class(String input1)
{
input = input1;
}
public boolean isVowel(int loc)
{
return (input.charAt(loc)=='U'||input.charAt(loc)=='O'||input.charAt(loc)=='I'||input.charAt(loc)=='E'||input.charAt(loc)=='A'||input.charAt(loc)=='a'||input.charAt(loc)=='e'||input.charAt(loc)=='i'||input.charAt(loc)=='o'||input.charAt(loc)=='u');
}
public boolean endWord(int loc)
{
return (input.charAt(loc)==' '||input.charAt(loc)=='.'||input.charAt(loc)=='?'||input.charAt(loc)=='!');
}
public void findWord(int beg,int end)
{
answer = answer+(input.substring(beg,end));
}
public void printAnswer()
{
System.out.println(answer+".");
}
}
With this code, i get the output:
Itisaa hotandand humidand humid summerand humid summer day.
By removing this piece of code:
&& (j == 0 || input.charAt(j-1) == ' ')
I get the proper output, but it doesn't work if an inputted word has more than one vowel in it.
For example:
input: Apples and bananas.
output: and.
Can someone please explain:
a) why the code is printing out words beginning with consonants as it is and
b) how I could fix it.
Also, the methods in the class I've written can't be changed.
Here's a better algorithm:
split the input into an array of words
iterate over each word
if the word begins with a vowel, append it to the output
The easiest way to split the input would be to use String.split().
Here's a simple implementation:
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
String input = console.nextLine();
String[] words = input.split(" ");
StringBuilder output = new StringBuilder();
for (String s : words) {
if (startsWithVowel(s)) {
output.append(s);
}
else {
output.append(getPunc(s));
}
}
System.out.println(output.toString());
}
public static boolean startsWithVowel(String s) {
char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
char firstChar = s.toLowerCase().charAt(0);
for (char v : vowels) {
if (v == firstChar) {
return true;
}
}
return false;
}
public static String getPunc(String s) {
if (s.matches(".*[.,:;!?]$")) {
int len = s.length();
return s.substring(len - 1, len);
}
return "";
}
The problem with your code was:
It was counting the same word multiple times, due to it finding vowels and starting the word search process over again.
Heres how I went about solving the problem, while still keeping your code looking relatively the same: All I changed was your loop
for(int i=0;i<input.length();i++)
{
if(strings.isVowel(i) &&(i==0 || strings.endWord(i-1))){
beg = i;
for(int j = i; j < input.length();j++) //look for end of word
{
if(strings.endWord(j)) //word has ended
{
i = j; //start from end of last word
strings.findWord(beg, j);
break; //word done, end word search
}
}
}
}
As mentioned above, there are better ways to go about this, and there are some pretty glaring flaws in the setup, but you wanted an answer, so here you go
Normally i would suggest you where to fix your code, but it's seems there is a lot of bad code practice in here.
Mass Concatenation should be apply be StringBuilder.
Never call a class Class
Conditions are too long and can be shorten by a static string of Vowels and apply .contains(Your-Char)
Spaces, Indentations required for readability purposes.
A different way of attacking this problem, may probably accelerate your efficiency.
Another approch will be Split the code by spaces and loop through the resulted array for starting vowels letters and then Append them to the result string.
A better readable and more maintainable version doing what you want:
public static String buildWeirdSentence(String input) {
Pattern vowels = Pattern.compile("A|E|I|O|U|a|e|i|o|u");
Pattern signs = Pattern.compile("!|\\.|,|:|;|\\?");
StringBuilder builder = new StringBuilder();
for (String word : input.split(" ")) {
String firstCharacter = word.substring(0, 1);
Matcher vowelMatcher = vowels.matcher(firstCharacter);
if (vowelMatcher.matches()) {
builder.append(word);
} else {
// we still might want the last character because it might be a sign
int wordLength = word.length();
String lastCharacter = word.substring(wordLength - 1, wordLength);
Matcher signMatcher = signs.matcher(lastCharacter);
if (signMatcher.matches()) {
builder.append(lastCharacter);
}
}
}
return builder.toString();
}
In use:
public static void main(String[] args) {
System.out.println(buildWeirdSentence("It is a hot and humid day.")); // Itisaand.
}
I think best approach is to split input and then check each word if it starts with vowel.
public static void main(String[] args)
{
Scanner console = new Scanner(System.in);
System.out.print("Input: ");
String str = console.next();
String[] input = str.split(" ");
StringBuilder s = new StringBuilder();
String test;
for (int i = 0; i < input.length; i++)
{
test = input[i];
if (test.charAt(0) == 'U' || test.charAt(0) == 'O'
|| test.charAt(0) == 'I' || test.charAt(0) == 'E'
|| test.charAt(0) == 'A' || test.charAt(0) == 'a'
|| test.charAt(0) == 'e' || test.charAt(0) == 'i'
|| test.charAt(0) == 'o' || test.charAt(0) == 'u')
{
s.append(input[i]);
}
}
System.out.println(s);
}
The problem with your code is that you override the first beg when a word has more that vowel. for example with Apples beg goes to 0 and before you could call findWord to catch it, it gets overridden with 4 which is the index of e. And this is what screws up your algorithm.
You need to note that you have already found a vowel until you have called finWord, for that you can add a boolean variable haveFirstVowel and set it the first time you have found one to true and only enter the branch for setting that variable to true if you haven't already set it. After you have called findWord set it back to false.
Next you need to detect the start of a word, otherwise for example the o of hot could wrongly signal a first vowel.
Class strings = new Class(input);
int beg = 0;
boolean haveFirstVowel = false;
for (int j = 0; j < input.length(); j++) {
boolean startOfWord = (beg == 0 || input.charAt(j - 1) == ' ');
if (startOfWord && ! haveFirstVowel && strings.isVowel(j)) {
beg = j;
haveFirstVowel = true;
}
else if (strings.endWord(j) && haveFirstVowel) {
strings.findWord(beg, j);
haveFirstVowel = false;
}
}
System.out.print("Output: ");
strings.printAnswer();
I think overall the algorithm is not bad. It's just that the implementation can definitely be better.
Regarding to the problem, you only need to call findWord() when:
You have found a vowel, and
You have reached the end of a word.
Your code forgot the rule (1), therefore the main() can be modified as followed:
Scanner console = new Scanner(System.in);
System.out.print("Input: ");
String input = console.nextLine();
Class strings = new Class(input);
int beg = 0;
boolean foundVowel = false; // added a flag indicating whether a vowel has been found or not
for (int j = 0; j < input.length(); j++) {
if (strings.isVowel(j) && (j == 0 || input.charAt(j - 1) == ' ')) {
beg = j;
foundVowel = true;
} else if (strings.endWord(j) && (beg == 0 || input.charAt(beg - 1) == ' ')) {
if (foundVowel) { // only call findWord() when you have found a vowel and reached the end of a word
strings.findWord(beg, j);
foundVowel = false; // remember to reset the flag
}
}
}
System.out.print("Output: ");
strings.printAnswer();

Categories

Resources