Recursive Java RLE Decompression Method [stackoverflow error] - java

I am trying to write a program in Java that decompresses a compressed RLE statement using recursion, but I am continuously getting stack overflow errors and I do not know why.
Here is what I wrote thus far:
public class StringRec
{
public static void main (String args[]){
System.out.println(decompress("wed4d"));
}
//Version 0.1
public static String decompress(String compressedText)
{ String cText = compressedText;
StringBuffer newString = new StringBuffer();
int i = 0;
if (cText==""){return newString.toString();}
if(!cText.isEmpty()){
if(Character.isLetter(cText.charAt(i))){
newString.append(cText.charAt(i));
cText = cText.substring(1,cText.length());
return decompress(cText);
//remove first letter, return new modified string with removed first letter to decompress.
}
if(Character.isDigit(cText.charAt(i))){
int c = cText.charAt(i)-'0';
if (c==0){
cText = cText.substring(2,cText.length());}
return decompress(cText);
//delete c and the letter after it and send new modified string to decompress.
}
}else {
newString.append(cText.charAt(i+1));
int c = cText.charAt(i);
c--;
String num = ""+c;
cText = cText.replaceFirst(num, Character.toString(cText.charAt(i)));
return decompress(cText);
//appends character after number to newString, decrements the number and returns
//the new modified string to decompress with the first number decremented by one
}
return newString.toString();
}
}
My base case for the recursion is an empty string, if the string starts with a letter that letter is added to the stringbuffer newString only once and that first letter of the original string is deleted from the string sequence and the new string is passed to decompress; if it starts with a number that is zero then the first two chars of the string are deleted and the new string is passed to decompress.
If it's a number greater than 0[else] then the letter in front of it is added to the stringbuffer newString and the number is decremented and replaces the number in the beginning of the string and passes the new string [with the original first char number - 1] to decompress.

I believe the infinite recursion is occurring because of this:
int c = cText.charAt(i);
c--;
String num = ""+c;
cText = cText.replaceFirst(num, Character.toString(cText.charAt(i)));
If the character is 1, then c will be 65, the ASCII value of the character '1'. Then num will be set to the string "64", and if there's no "64" in your string, the method will keep calling itself with the same string over and over. Declaring c as a char should fix that. Plus, note that replaceFirst says to search for a pattern that matches the first argument, and replace it with the second. You may have this backwards. Also, see my comment above about newString.
EDIT:To answer your comment: Using a StringBuffer is OK. What you cannot do is have each decompress create a new StringBuffer, because then each time decompress is called a new StringBuffer will be created, which isn't what you want. If decompress takes a StringBuffer newString parameter, and each time decompress calls itself it passes newString as a parameter, then every decompress call will point to the same StringBuffer, since it's a reference to an object.
The other approach that I think you hinted at was that since decompress returns a String, then every time decompress calls itself it can use the function result (right now your code is just throwing it away), and perhaps concatenate that function result with something else and return the new string. I think you can make this approach work, but I haven't studied it thoroughly.

Related

Replacing a character in a string from another string with the same char index

I'm trying to search and reveal unknown characters in a string. Both strings are of length 12.
Example:
String s1 = "1x11222xx333";
String s2 = "111122223333"
The program should check for all unknowns in s1 represented by x|X and get the relevant chars in s2 and replace the x|X by the relevant char.
So far my code has replaced only the first x|X with the relevant char from s2 but printed duplicates for the rest of the unknowns with the char for the first x|X.
Here is my code:
String VoucherNumber = "1111x22xx333";
String VoucherRecord = "111122223333";
String testVoucher = null;
char x = 'x'|'X';
System.out.println(VoucherNumber); // including unknowns
//find x|X in the string VoucherNumber
for(int i = 0; i < VoucherNumber.length(); i++){
if (VoucherNumber.charAt(i) == x){
testVoucher = VoucherNumber.replace(VoucherNumber.charAt(i), VoucherRecord.charAt(i));
}
}
System.out.println(testVoucher); //after replacing unknowns
}
}
I am always a fan of using StringBuilders, so here's a solution using that:
private static String replaceUnknownChars(String strWithUnknownChars, String fullStr) {
StringBuilder sb = new StringBuilder(strWithUnknownChars);
while ((int index = Math.max(sb.toString().indexOf('x'), sb.toString().indexOf('X'))) != -1) {
sb.setCharAt(index, fullStr.charAt(index));
}
return sb.toString();
}
It's quite straightforward. You create a new string builder. While a x or X can still be found in the string builder (indexOf('X') != -1), get the index and setCharAt.
Your are using String.replace(char, char) the wrong way, the doc says
Returns a new string resulting from replacing all occurrences of oldChar in this string with newChar.
So you if you have more than one character, this will replace every one with the same value.
You need to "change" only the character at a specific spot, for this, the easiest is to use the char array that you can get with String.toCharArray, from this, this is you can use the same logic.
Of course, you can use String.indexOf to find the index of a specific character
Note : char c = 'x'|'X'; will not give you the expected result. This will do a binary operation giving a value that is not the one you want.
The OR will return 1 if one of the bit is 1.
0111 1000 (x)
0101 1000 (X)
OR
0111 1000 (x)
But the result will be an integer (every numeric operation return at minimum an integer, you can find more information about that)
You have two solution here, you either use two variable (or an array) or if you can, you use String.toLowerCase an use only char c = 'x'

Getting error "String index out of range: 0" on using String Builder

So this is my code in Java (for returning two halves of a string - one is the odd half, starting with the index 0, or the first character, and the second half, starting with the index 1, or the second character):
public class StringTest{
public String halfOfString(String message, int start){
int length = message.length();
StringBuilder output = new StringBuilder(length);
char ch;
int i;
if((start==0)||(start==1)){
for(i=start; i<message.length(); i=i+2){
ch = message.charAt(i);
output.setCharAt(i,ch); // error occurs here
}
}
return output.toString();
}
public void testFunction(){
String s = "My name is Sid";
String first = halfOfString(s, 0);
String second = halfOfString(s, 1);
System.out.println("First half is " + first + " and second half is " + second);
}
}
So my problem is - whenever I attempt to run this program on BlueJ IDE, it doesn't, and returns the error in the title, on the line mentioned in the comment.
I have poured over this site for a similar question which may help me with my error, but all I found was a question which suggested a change I have already implemented (in the StringBuilder setCharAt method, the person had reversed the i and ch parameters).
Is it anything to do with the fact that the "output" is declared empty at first, and the setCharAt method can only replace the characters which already exist?
Any help would be greatly appreciated.
Is it anything to do with the fact that the "output" is declared empty at first, and the setCharAt method can only replace the characters which already exist?
Yes, that is exactly why you get this error.
Note that creating a StringBuilder with a specified length, as you are doing:
StringBuilder output = new StringBuilder(length);
does not create a StringBuilder which already has that many characters - it just creates a StringBuilder with that internal buffer size. The StringBuilder itself still contains no characters, so trying to set the first character will result in the exception that you get.
Call append on the StringBuilder to add characters instead of setCharAt.
Is it anything to do with the fact that the "output" is declared empty
at first, and the setCharAt method can only replace the characters
which already exist?
Yes, that's the reason.
The Javadoc of setCharAt is clear on this:
The index argument must be greater than or equal to 0, and less than
the length of this sequence.
Why don't you just use the append method in StringBuilder? It looks to me like, if your code would work, it would leave holes in the StringBuilder since you're only setting every other character and not the ones in between. Using append ensures that there are no holes.

Array Index out of Bound Exception for returning Char Array

I am new to Java programming and I was writing code to replace spaces in Strings with %20 and return the final String. Here is the code for the problem. Since I am new to programming please tell me what I did wrong. Sorry for my bad English.
package Chapter1;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Problem4 {
public char[] replaceSpaces(char[] str_array, int length)
{
int noOfSpaces=0,i,newLength;
for(i=0;i<length;i++)
{
if(str_array[i]==' ')
{
noOfSpaces++;
}
newLength = length + noOfSpaces * 2;
str_array[newLength]='\0';
for(i=0;i<length-1;i++)
{
if(str_array[i]==' ')
{
str_array[newLength-1]='0';
str_array[newLength-2]='2';
str_array[newLength-3]='%';
newLength = newLength-3;
}
str_array[newLength-1]=str_array[i];
newLength = newLength - 1;
}
}
return str_array;
}
public static void main(String args[])throws Exception
{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Please enter the string:");
String str = reader.readLine();
char[] str_array = str.toCharArray();
int length = str.length();
Problem4 obj = new Problem4();
char[] result = obj.replaceSpaces(str_array, length);
System.out.println(result);
}
}
But I get the following error:
Please enter the string:
hello world
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 11
at Chapter1.Problem4.replaceSpaces(Problem4.java:19)
at Chapter1.Problem4.main(Problem4.java:46)
How about using String.replaceAll():
String str = reader.readLine();
str = str.replaceAll(" ", "02%");
Sample code here
EDIT:
The problem is at line 19:
str_array[newLength]='\0';//<-- newLength exceeds the char array size
Here array is static i.e. the size is fixed you can use StringBuilder, StringBuffer, etc to build the new String without worrying about the size for such small operations.
Assuming that you want to see what mistakes you made when implementing your approach, instead of looking for a totally different approach:
(1) As has been pointed out, once an array has been allocated, its size cannot be changed. Your method takes str_array as a parameter, but the resulting array will likely be larger than str_array. Therefore, since str_array's length cannot be changed, you'll need to allocate a new array to hold the result, rather than using str_array. You've computed newLength correctly; allocate a new array of that size:
char[] resultArray = new char[newLength];
(2) As Elliott pointed out, Java strings don't need \0 terminators. If, for some reason, you really want to create an array that has a \0 character at the end, then you have to add 1 to your computed newLength to account for the extra character.
(3) You're actually creating the resulting array backward. I don't know if that is intentional.
if(str_array[i]==' ')
{
str_array[newLength-1]='0';
str_array[newLength-2]='2';
str_array[newLength-3]='%';
newLength = newLength-3;
}
str_array[newLength-1]=str_array[i];
newLength = newLength - 1;
i starts with the first character of the string and goes upward; you're filling in characters starting with the last character of the string (newLength) and going backward. If that's what you intended to do, it wasn't clear from your question. Did you want the output to be "dlrow%20olleh"?
(4) If you did intend to go backward, then what the above code does with a space is to put %20 in the string (backwards), but then it also puts the space into the result. If the input character is a space, you want to make sure you don't execute the two lines that copy the input character to the result. So you'll need to add an else. (Note that this problem will lead to an out-of-bounds error, because you're trying to put more characters into the result than you computed.) You'll need to have an else in there even if you really meant to build the string forwards and need to change the logic to make it go forward.
Java arrays are not dynamic (they are Object instances, and they have a field length property that does not change). Because they store the length as a field, it is important to know that they're not '\0' terminated (your attempt to add such a terminator is causing your index out of bounds Exception). Your method doesn't appear to access any instance fields or methods, so I'd make it static. Then you could use a StringBuilder and a for-each loop. Something like
public static char[] replaceSpaces(char[] str_array) {
StringBuilder sb = new StringBuilder();
for (char ch : str_array) {
sb.append((ch != ' ') ? ch : "%20");
}
return sb.toString().toCharArray();
}
Then call it like
char[] result = replaceSpaces(str_array);
Finally, you might use String str = reader.readLine().replace(" ", "+"); or replaceAll(" ", "%20") as suggested by #Arvind here.
P.S. When you finally get your result you'll need to fix your call to print it.
System.out.println(Arrays.toString(result));
or
System.out.println(new String(result));
A char[] is not a String and Java arrays (disappointingly) don't override toString() so you'll get the one from Object.
please tell me what I did wrong
You tried to replace a single character with three characters %20. That's not possible because arrays are fixed length.
Therefore you must allocate a new char[] and copy the characters from str_array into the new array.
for (i = 0; i < length; i++) {
if (str_array[i] == ' ') {
noOfSpaces++;
}
}
newLength = length + noOfSpaces * 2;
char[] newArray = new char[newLength];
// copy characters from str_array into newArray
The exception is raised in this line str_array[newLength]='\0'; because value of newLength is greater than length of str_array.
Array size cannot be increased once it is defined. So try the alternative solution.
char[] str_array1=Arrays.copyOf(str_array, str_array.length+1);
str_array1[newLength]='\0';
don't forget to import the new package import java.util.Arrays;

Run-length decompression

CS student here. I want to write a program that will decompress a string that has been encoded according to a modified form of run-length encoding (which I've already written code for). For instance, if a string contains 'bba10' it would decompress to 'bbaaaaaaaaaa'. How do I get the program to recognize that part of the string ('10') is an integer?
Thanks for reading!
A simple regex will do.
final Matcher m = Pattern.compile("(\\D)(\\d+)").matcher(input);
final StringBuffer b = new StringBuffer();
while (m.find())
m.appendReplacement(b, replicate(m.group(1), Integer.parseInt(m.group(2))));
m.appendTail(b);
where replicate is
String replicate(String s, int count) {
final StringBuilder b = new StringBuilder(count);
for (int i = 0; i < count; i++) b.append(s);
return b.toString();
}
Not sure whether this is one efficient way, but just for reference
for (int i=0;i<your_string.length();i++)
if (your_string.charAt(i)<='9' && your_string.charAt(i)>='0')
integer_begin_location = i;
I think you can divide chars in numeric and not numeric symbols.
When you find a numeric one (>0 and <9) you look to the next and choose to enlarge you number (current *10 + new) or to expand your string
Assuming that the uncompressed data does never contain digits: Iterate over the string, character by character until you get a digit. Then continue until you have a non-digit (or end of string). The digits inbetween can be parsed to an integer as others already stated:
int count = Integer.parseInt(str.substring(start, end));
Here is a working implementation in python. This also works fine for 2 or 3 or multiple digit numbers
inputString="a1b3s22d4a2b22"
inputString=inputString+"\0" //just appending a null char
charcount=""
previouschar=""
outputString=""
for char in inputString:
if char.isnumeric():
charcount=charcount+char
else:
outputString=outputString
if previouschar:
outputString=outputString+(previouschar*int(charcount))
charcount=""
previouschar=char
print(outputString) // outputString= abbbssssssssssssssssssssssddddaabbbbbbbbbbbbbbbbbbbbbb
Presuming that you're not asking about the parsing, you can convert a string like "10" into an integer like this:
int i = Integer.parseInt("10");

How can I extract the numbers from a string only using charAt(), length() and/or toCharArray() in Java

I have to do this for an assignment in my java class. I have been searching for a while now, but only find solutions with regex etc.
For my assignment however I may only use charAt(), length() and/or toCharArray(). I need to get from a string like gu578si300 for example just the numbers so it will become: 578300.
i know numbers are 48 - 57 in ASCII but i can't figure out how to do this in java. You guys any ideas?
i was thinking about a for loop that checks whether the (int) char is between 48-57 en if so puts the value into a seperate array. Howeevr i dont know how to programm that last thing.
I now have this;
public static String filterGetallenreeks(String reeks){
String temp = "";
for (char c : reeks.toCharArray()) {
if ((int) c > 47 && (int) c < 58)
temp += c;
}
return temp;
however it is not working, it just outputs the same as goes in.
is it something in my mainm which looks like this. If i'm right the return temp; will return the temp string into the reeks string in the main right? why is my input still the same a sthe output?
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Voer een zin, woord of cijferreeks in:");
String reeks = sc.nextLine();
if (isGetallenreeks(reeks)){
System.out.println("is getallenreeks");
filterGetallenreeks(reeks);
System.out.println(reeks);
}
Since this is homework I will not be providing the complete solution, however, this is how you should go about it:
Do a for loop that iterates for the total amount of characters within the string (.length). Check if the character is a digit using the charAt and isDigit methods.
You could do a loop that checks a character in the string, and if it's a number, append it to another string:
//I haven't tested this, so you know.
String test = "gu578si300 ";
String numbers = "";
for(int i=0; i<test.length(); i++){
if("0123456789".indexOf(test.charAt(i)) // if the character at position i is a number,
numbers = numbers + test.charAt(i); // Add it to the end of "numbers".
}
int final = Integer.parseInt(numbers); // If you need to do something with those numbers,
// Parse it.
Let me know if that works for you.
It seems like a reasonable approach, but I'd make a couple of changes from what you suggested:
If you need to result as a string then use a StringBuilder instead of an array.
Use character literals like '0' and '9' instead of ASCII codes to make your code more readable.
Update
The specific problem with your code is this line:
temp = temp + (int)c;
This converts the character to its ASCII value and then converts that to a decimal string containing the ASCII value. That's not what you want. Use this instead:
temp += c;

Categories