I have a String entered by the User.
I'm trying to replace all non-uppercase letters(i.e. numbers, or symbols) with spaces without using methods such as replace(), Stringbuilders, or arrays.
This is what I have so far :
public static void formatPlaintext(String sentence){
String sentenceUpper = sentence.toUpperCase();
System.out.println(sentenceUpper);
String emptyString = " ";
for(int i = 0; i< sentenceUpper.length() ; i++){
char ch = sentenceUpper.charAt(i);
if(ch < 'A' || ch > 'Z'){
ch = emptyString.charAt(0);
}
}
}//end of formatPlaintext
I keep getting the error that String index is out of range. I believe it has to do with :
ch = emptyString.charAt(0);
because emptyString doesn't have any characters. But even if I put an arbitrary constant in, it doesn't replace the non-letters with this arbitrary constant.
This isn't how you replace characters in a Java string. In Java, strings are immutable, so you can't set any given index. Additionally, the charAt() method doesn't and can't do anything to the string you're calling it on - all it does is just return the char at that position. Lastly, you probably shouldn't be using void - return the String with characters replaced at the end of the method, then use the return value. You can accomplish this by iterating through your initial string and build a new string, using the static isUpperCase method of the Character class:
public static String formatPlainText(String sentence)
{
String replacedSentence = "";
for(int i = 0; i< sentence.length() ; i++){
char ch = sentence.charAt(i);
if (Character.isUpperCase(ch)) {
replacedSentence += ch;
}
else {
replacedSentence += " ";
}
}
return replacedSentence;
}
If you're going to be using this frequently, or for particularly long Strings, you should absolutely use a StringBuilder instead - concatenating String on a character-by-character basis is deeply inefficient.
You have to remember that arguments in Java are passed as values, not as references, and in this case the String is an immutable object, i.e. an String cannot be changed, when you do a concatenation or replace you're effectively creating a new String.
What I would recommend is to modify your code a little bit to return the new String that was built.
public static String formatPlaintext(String sentence){
String sentenceUpper = sentence.toUpperCase();
System.out.println(sentenceUpper);
StringBuilder builder = new StringBuilder;
for(int i = 0; i< sentenceUpper.length() ; i++){
char ch = sentenceUpper.charAt(i);
if(ch < 'A' || ch > 'Z'){
builder.append(' ');
} else {
builder.append(ch);
}
}
return builder.toString();
}
Related
public static String replaceExclamation(String userText){
int i = 0;
for ( i=0; i < userText.length(); ++i) {
char currentChar = userText.charAt(i);
if (currentChar == '!') {
userText.charAt(i) = ".";
}
}
return userText;
}
I am trying to replace all the '!' in a string with a '.' but I am getting an unexpected type error.
What does this mean and how can I fix it?
Also, does the fact that userText is from main instead of this local method impact my ability to edit the string?
String is immutable, if you replace any character in String then with that change new String object is created, so i prefer using StringBuilder for this
public static StringBuilder replaceExclamation(StringBuilder userText){
int i = 0;
for ( i=0; i < userText.length(); ++i) {
char currentChar = userText.charAt(i);
if (currentChar == '!') {
userText.setCharAt(i,'.');
}
}
return userText;
}
Or you can use replace(char oldChar, char newChar)
String result = userText.replace('!', '.');
Or you can use replaceAll(String regex, String replacement)
String result = userText.replaceAll("!", ".");
I'm doing a caesar-cypher. Trying to replace all characters from a string to a certain character from the shifted alphabet.
Here is my code so far
public static String caesarify(String str, int key){
String alphabetNormal = shiftAlphabet(0);
String alphabetShifted = shiftAlphabet(key);
for (int i =0; i < str.length();i++){
for (int c =0; c < alphabetNormal.length(); c++) {
if (str.charAt(i) == alphabetNormal.charAt(c)) {
char replacement = alphabetShifted.charAt(c);
str.replace(str.charAt(i), replacement);
}
}
}
return str;
}
public static String shiftAlphabet(int shift) {
int start =0;
if (shift < 0) {
start = (int) 'Z' + shift + 1;
} else {
start = 'A' + shift;
}
String result = "";
char currChar = (char) start;
for(; currChar <= 'Z'; ++currChar) {
result = result + currChar;
}
if(result.length() < 26) {
for(currChar = 'A'; result.length() < 26; ++currChar) {
result = result + currChar;
}
}
return result;
}
I don't know why the string for example "ILIKEDONUTS" doesn't change to "JMJLFEPOVUT" when it's caesarified.
Don't use replace(), or any replace method, to replace a character at a given index in a String. It doesn't work. You're hoping that
str.replace(str.charAt(i), replacement);
will replace the i'th character of str. As pointed out in the other (now deleted) answer, str.replace doesn't change str itself, so you'd need to write
str = str.replace(str.charAt(i), replacement);
But that doesn't work. The replace() method doesn't know what your index i is. It only knows what character to replace. And, as the javadoc for replace() says, it replaces all characters in the string. So suppose that str.charAt(i) is 'a', and you want to replace it with 'd'. This code would replace all a characters with d, including (1) those that you already replaced with a earlier in the loop, so that this will defeat the work you've already done; and (2) a characters that come after this one, which you want to replace with d, but this will fail because later in the loop you will see d and replace it with g.
So you can't use replace(). There are a number of ways to replace the i'th character of a string, including using substring():
str = str.substring(0, i) + replacement + str.substring(i + 1);
But there are better ways, if you are going to replace every character. One is to create a StringBuilder from str, use StringBuilder's setCharAt method to change characters at specified indexes, and then convert the StringBuilder back to a String at the end. You should be able to look at the javadoc to find out what methods to use.
More: After looking into this more, I see why it was returning all A's. This inner loop has an error:
for (int c =0; c < alphabetNormal.length(); c++) {
if (str.charAt(i) == alphabetNormal.charAt(c)) {
char replacement = alphabetShifted.charAt(c);
str.replace(str.charAt(i), replacement);
}
}
Suppose key is 1, and the current character is 'C'. Your inner loop will eventually find C in alphabetNormal; it finds the corresponding character in alphabetShifted, which is D, and replaces C with D.
But then it loops back. Since the next character in alphabetNormal is D, it now matches the new str.char(i), which is now D, and therefore changes it again, to E. Then it loops back, and ... you get the picture.
replace below line
str.replace(str.charAt(i), replacement);
With
str= str.replace(str.charAt(i), replacement);
or you can make a String arr and then replace character in that. in the end create a new string from that array and return.
a better version of caesarify():
public static String caesarify(String str, int key){
String alphabetNormal = shiftAlphabet(0);
String alphabetShifted = shiftAlphabet(key);
//create a char array
char[] arr=str.toCharArray();
//always try to create variable outside of loop
char replacement
for (int i =0; i < arr.length();i++){
for (int c =0; c < alphabetNormal.length(); c++) {
if (arr[i] == alphabetNormal.charAt(c)) {
replacement = alphabetShifted.charAt(c);
//replace char on specific position in the array
arr[i]= replacement;
}
}
}
//return arrray as String
return new String(arr);
}
Im very new to coding and cant seem to be able to return anything. I need to convert upper case characters to lower case and vice versa. Here's my code:
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
invString(str);
sc.close();
}
private static String invString(String str) {
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch > 97) {
ch = Character.toUpperCase(ch);
return str;
} else {
ch = Character.toLowerCase(ch);
return str;
}
}
return null;
}
What am i doing wrong? ( in terms of returning, the code isnt complete yet)
EDIT****************
thanks for the helpful remarks, as i understood i do not have a place where my modifications are stored, so i added String res = ""; and kept adding the values into String res but in the end, when returning res, i still dont get any output...Here is the whole thing:
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String st = invString(str);
sc.close();
}
private static String invString(String str) {
String res = "";
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (65 <= ch && ch <= 90) {
ch += 32;
res += ch;
} else if (97 <= ch && ch <= 122) {
ch -= 32;
res += ch;
}
}
return res;
}
ps. Im not using the ready methods because my task asks for it.
There are a number of flaws in your code. Firstly, you are attempting to return the original string str in every if statement. So what happens is the method invString( String ) simply returns the original string that is passed as argument. Instead, you should keep adding the characters to a new String variable (as #Massimo said). Also, you are returning null in the end. Why even do that? You would want to return the new String variable instead.
private static String invString(String str) {
String s=""; //new String variable
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch > 97) {
ch = Character.toUpperCase(ch);
s+=ch; //do this instead of returning str
} else {
ch = Character.toLowerCase(ch);
s+=ch; //same here
}
}
return s; //return the new String
}
Secondly, in your main method, simply calling the method is wrong as it returns a value. You should assign the value returned by invString() to a String variable.
public static void main(String[] args){
...
String st = invString(str); //st stores the value of str with
//letters' cases changed
}
You return you str object without updating it at all.
You should generate a new string in which put the characters for which you reverse the case.
Check the last answers in
How can I invert the case of a String in Java?
If you want to use the inverted string, you need to actually use the returned value, e.g:
str = invString (str)
Strings are immutable so you can't change the characters within them. You can only replace the string variable with a new string.
Modifying the characters you are accessing doesn't affect the string. You need to build a new string (look up StringBuilder) and return it.
I'm trying to return strings in different lines given these conditions. Since I cannot use the += in Java with strings, how do I make one giant string that is spaced per line but "stacks?" In other words, how do I add a new string within a loop to an old string?
/**
Returns a String that concatenates all "offending"
words from text that contain letter; the words are
separated by '\n' characters; the returned string
does not contain duplicate words: each word occurs
only once; there are no punctuation or whitespace
characters in the returned string.
#param letter character to find in text
#return String containing all words with letter
*/
public String allWordsWith(char letter)
{
String result = "";
int i = 0;
while (i < text.length())
{
char newchar = text.charAt(i);
if (newchar == letter)
{
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
String newstring = '\n' + text.substring(index2,index1);
}
i++;
}
return result;
}
Modify the result string, and fix your "word boundary" tests.
if (newchar == letter) {
int index1 = text.lastIndexOf(' ',i);
int index2 = text.indexOf(' ',i);
// TODO -- handle when index1 or index2 is < 0; that means it wasn't found,
// and you should use the string boundary (0 or length()) instead.
String word = text.substring( index2,index1);
result += "\n" + word;
}
If you were really concerned about performance you could use a StringBuilder and append(), but otherwise I strongly favour += for being concise & readable.
you are re-initializing your string in loop every time. Move the string declaration outsid eof loop:
Replace this
String newstring = '\n' + text.substring(index2,index1);
with
result = '\n' + text.substring(index2,index1);
First, use a StringBuilder.
Second, use System.getProperty("line.separator") to ensure proper line breaks are used.
Edited code:
public String allWordsWith(char letter)
{
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < text.length())
{
char newchar = text.charAt(i);
if (newchar == letter)
{
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
sb.Append(text.substring(index2,index1));
sb.Append(System.getProperty("line.separator"));
//I put the new line after the word so you don't get an empty
//line on top, but you can do what you need/want to do in this case.
}
i++;
}
return result;
}
Use StringBuilder as following:
public String allWordsWith(char letter){
//String result = "";
StringBuilder result = new StringBuilder();
int i = 0;
while (i < text.length()){
char newchar = text.charAt(i);
if (newchar == letter){
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
result.append('\n' + text.substring(index2,index1));
}
i++;
}
return result.toString();
}
String text = "I have android code with many different java, bmp and xml files everywhere in my project that I used during the drafting phase of my project.";
String letter = "a";
Set<String> duplicateWordsFilter = new HashSet<String>(Arrays.asList(text.split(" ")));
StringBuilder sb = new StringBuilder(text.length());
for (String word : duplicateWordsFilter) {
if (word.contains(letter)) {
sb.append(word);
sb.append("\n");
}
}
return sb.toString();
result is:
android
have
java,
drafting
and
many
that
phase
String handling in Java is something I'm trying to learn to do well. Currently I want to take in a string and replace any characters I find.
Here is my current inefficient (and kinda silly IMO) function. It was written to just work.
public String convertWord(String word)
{
return word.toLowerCase().replace('á', 'a')
.replace('é', 'e')
.replace('í', 'i')
.replace('ú', 'u')
.replace('ý', 'y')
.replace('ð', 'd')
.replace('ó', 'o')
.replace('ö', 'o')
.replaceAll("[-]", "")
.replaceAll("[.]", "")
.replaceAll("[/]", "")
.replaceAll("[æ]", "ae")
.replaceAll("[þ]", "th");
}
I ran 1.000.000 runs of it and it took 8182ms. So how should I proceed in changing this function to make it more efficient?
Solution found:
Converting the function to this
public String convertWord(String word)
{
StringBuilder sb = new StringBuilder();
char[] charArr = word.toLowerCase().toCharArray();
for(int i = 0; i < charArr.length; i++)
{
// Single character case
if(charArr[i] == 'á')
{
sb.append('a');
}
// Char to two characters
else if(charArr[i] == 'þ')
{
sb.append("th");
}
// Remove
else if(charArr[i] == '-')
{
}
// Base case
else
{
sb.append(word.charAt(i));
}
}
return sb.toString();
}
Running this function 1.000.000 times takes 518ms. So I think that is efficient enough. Thanks for the help guys :)
You could create a table of String[] which is Character.MAX_VALUE in length. (Including the mapping to lower case)
As the replacements got more complex, the time to perform them would remain the same.
private static final String[] REPLACEMENT = new String[Character.MAX_VALUE+1];
static {
for(int i=Character.MIN_VALUE;i<=Character.MAX_VALUE;i++)
REPLACEMENT[i] = Character.toString(Character.toLowerCase((char) i));
// substitute
REPLACEMENT['á'] = "a";
// remove
REPLACEMENT['-'] = "";
// expand
REPLACEMENT['æ'] = "ae";
}
public String convertWord(String word) {
StringBuilder sb = new StringBuilder(word.length());
for(int i=0;i<word.length();i++)
sb.append(REPLACEMENT[word.charAt(i)]);
return sb.toString();
}
My suggestion would be:
Convert the String to a char[] array
Run through the array, testing each character one by one (e.g. with a switch statement) and replacing it if needed
Convert the char[] array back to a String
I think this is probably the fastest performance you will get in pure Java.
EDIT: I notice you are doing some changes that change the length of the string. In this case, the same principle applies, however you need to keep two arrays and increment both a source index and a destination index separately. You might also need to resize the destination array if you run out of target space (i.e. reallocate a larger array and arraycopy the existing destination array into it)
My implementation is based on look up table.
public static String convertWord(String str) {
char[] words = str.toCharArray();
char[] find = {'á','é','ú','ý','ð','ó','ö','æ','þ','-','.',
'/'};
String[] replace = {"a","e","u","y","d","o","o","ae","th"};
StringBuilder out = new StringBuilder(str.length());
for (int i = 0; i < words.length; i++) {
boolean matchFailed = true;
for(int w = 0; w < find.length; w++) {
if(words[i] == find[w]) {
if(w < replace.length) {
out.append(replace[w]);
}
matchFailed = false;
break;
}
}
if(matchFailed) out.append(words[i]);
}
return out.toString();
}
My first choice would be to use a StringBuilder because you need to remove some chars from the string.
Second choice would be to iterate throw the array of chars and add the treated char to another array of the inicial size of the string. Then you would need to copy the array to trim the possible unused positions.
After that, I would make some performance tests to see witch one is better.
I doubt, that you can speed up the 'character replacement' at all really. As for the case of regular expression replacement, you may compile the regexs beforehand
Use the function String.replaceAll.
Nice article similar with what you want: link
Any time we have problems like this we use regular expressions are they are by far the fastest way to deal with what you are trying to do.
Have you already tried regular expressions?
What i see being inefficient is that you are gonna check again characters that have already been replaced, which is useless.
I would get the charArray of the String instance, iterate over it, and for each character spam a series of if-else like this:
char[] array = word.toCharArray();
for(int i=0; i<array.length; ++i){
char currentChar = array[i];
if(currentChar.equals('é'))
array[i] = 'e';
else if(currentChar.equals('ö'))
array[i] = 'o';
else if(//...
}
I just implemented this utility class that replaces a char or a group of chars of a String. It is equivalent to bash tr and perl tr///, aka, transliterate. I hope it helps someone!
package your.package.name;
/**
* Utility class that replaces chars of a String, aka, transliterate.
*
* It's equivalent to bash 'tr' and perl 'tr///'.
*
*/
public class ReplaceChars {
public static String replace(String string, String from, String to) {
return new String(replace(string.toCharArray(), from.toCharArray(), to.toCharArray()));
}
public static char[] replace(char[] chars, char[] from, char[] to) {
char[] output = chars.clone();
for (int i = 0; i < output.length; i++) {
for (int j = 0; j < from.length; j++) {
if (output[i] == from[j]) {
output[i] = to[j];
break;
}
}
}
return output;
}
/**
* For tests!
*/
public static void main(String[] args) {
// Example from: https://en.wikipedia.org/wiki/Caesar_cipher
String string = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG";
String from = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String to = "XYZABCDEFGHIJKLMNOPQRSTUVW";
System.out.println();
System.out.println("Cesar cypher: " + string);
System.out.println("Result: " + ReplaceChars.replace(string, from, to));
}
}
This is the output:
Cesar cypher: THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
Result: QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD