Java: How to fix "String index out of range: #"? [duplicate] - java

This question already has answers here:
Java substring: 'string index out of range'
(13 answers)
Closed 3 years ago.
I'm attempting to scramble two random letters of a string which isn't the first letter, or last two. I'm getting a "String index out of range" error when compiling. I've tried workshopping many different solutions, but nothing seems to work.
For this assignment, we have to use a method and .charAt commands. I've tried creating variables for the two random characters then adding them back into the string flipped, but couldn't get that to work either.
public static String scramble(String input) {
int range = input.length() - 3;
int place = (int)(Math.random() * range);
String newWord = "";
newWord = input.substring(0, place);
newWord = newWord + newWord.charAt(place) + 2;
newWord = newWord + newWord.charAt(place) + 1;
return newWord;
I'm expecting an output of a string with two of its characters scrambled. For example, "Fantastic" would be "Fantsatic", or "Fnatastic".

Try something like this:
public static String scramble(String input) {
int range = input.length() - 3;
int place = (int)(Math.random() * range);
String newWord = input.substring(0, place);
newWord = newWord + input.charAt(place + 1);
newWord = newWord + input.charAt(place);
// if you need the whole input, just 2 characters exchanged, uncomment this next line
// newWord = newWord + input.substring(place + 2, range);
return newWord;
}

You do :
newWord = input.substring(0, place);
So the indexes inside newWord goes from 0 to place-1
Then you do:
newWord.charAt(place);
But this index does not exist in your String. It is Out of Bound
See the doc

When you create newWord = input.substring(0, place) it has exactly place characters. You can't request charAt(place) from it, the last character is at place-1.
If you want to swap characters convert input to char[] and generate random indexes to swap.
String input = "Fantastic";
// random constraints
int min = 1;
int max = input.length() - 3;
// random two characters to swap
int from = min + (int) (Math.random() * max);
int to;
do {
to = min + (int) (Math.random() * max);
} while (to == from); // to and from are different
// swap to and from in chars
char[] chars = input.toCharArray();
char tmp = chars[from];
chars[from] = chars[to];
chars[to] = tmp;
String result = new String(chars);
System.out.println(result); // Ftntasaic

you can try
public static String scramble(String input) {
if(input.length() >3){
int range = input.length() - 3;
int place = 1+ new Random().nextInt(range) ;
input=input.substring(0, place) +input.charAt(place + 1)+input.charAt(place) +input.substring( place+2);
}
return input;
}
input: Fantastic
output : Fanatstic , Fatnastic ,Fanatstic

Related

Java - Reverse String null error

The input is meant to appear like this, example.
\n
Kazan R
\n
6789
\n
Nzk462
\n
However the output I receive looks like this
kzn462nullnzk
Why is this? and How can i solve it?
private void btnGenerateActionPerformed(java.awt.event.ActionEvent evt) {
secondname = JOptionPane.showInputDialog("Enter your surname:");
firstname = JOptionPane.showInputDialog("Enter your firstname:");
idno = JOptionPane.showInputDialog("Enter your idno:");
nametag = firstname.substring(0, 1);
initials = secondname + " " + nametag;
int randnum;
do {
randnum = (int) (Math.random() * 900) + 100;
} while (randnum % 2 != 0);
code = secondname.replaceAll("[aeiou || AEIOU](?!\\b)", "")+randnum ;
txaDisplay.append(initials + '\n' + idno.substring(6,10) + '\n' + code);
int length = secondname.length();
for (int i = length - 1; i >= 0; i--) {
reverse = reverse + secondname.charAt(i);
}
String end = reverse + code;
txaDisplay.append( reverse);
Why don't you use
new StringBuilder(secondname).reverse().toString()
to reverse your String? It's better, simple and more maintanable.
Get the character array from your source string
Create a new char array of same length
Start iterating from 0 to (sourceStringLength-1)
In each iteration, get the last character
from the end in your source array and populate in your new array
Create a new string from this new array
String source = "abcdefg";
char[] chars = source.toCharArray();
char[] reverseChars = new char[source.length()];
int len = source.length();
for(int i= 0; i < len; i++){
reverseChars[i] = chars[len-1-i];
}
String reverse = new String(reverseChars);
System.out.println(reverse);
Since You don't want to use StringBuilder/StringBuffer.
Try this
String reversedString="";
for(int i=inputString.length-1;i>=0;){
reversedString+=inputString.charAt(i--);
}
I think the problem is your definition of reverse, maybe you have something like:
String reverse;
Then you don't initialize your "reverse" so when your program makes the first concatenation in your loop, it looks like this:
reverse = null + secondname.charAt(i);
The null value is converted to a string so it can be visible in the output.
I hope this information helps you.
Good Luck.

How to start replacing characters at some point?

It was very hard to form the question and I am sure it is still not clear.
I have a CSV file e.g.: Firstname;Lastname;Adress;product1;product2;product3;product4;
I would like to start replacing ";" with "::". The problem is, I want to start replacing after third semicolon.
I know it can be done in while loop where I check every character, when semicolon occurs I will count +1 and if counter is 3, I will start replacing. But isn't there a way how to do it without a loop?
You can use indexOf(char,fromIndex) method.
Your third semicolon position search can be inlined :
csvLine.indexOf(';', csvLine.indexOf(';', csvLine.indexOf(';') + 1) + 1)
We assume that our csvLine has a least 3 semi-colons...
String csvLine = "Firstname;Lastname;Adress;product1;product2;product3;product4";
//Index of "fromIndex" param is inclusive, that's why we need to add 1
int pos = csvLine.indexOf(';', csvLine.indexOf(';', csvLine.indexOf(';') + 1) + 1);
//Retrieve string from the char after the third semi-colon
String truncatedLine = csvLine.substring(pos + 1);
//Replace ";" by "::" on our substring
truncatedLine = truncatedLine.replaceAll(";", "::");
//Then concat the first part of csvLine with the second
String result = csvLine.substring(0, pos + 1).concat(truncatedLine);
System.out.println(result); //Print => Firstname;Lastname;Adress;product1::product2::product3::product4
Poor input control and performance but we don't have any loops :)
If I have understood what you want, try this.
First search se position of the third semicolon:
String csvContent = "Firstname;Lastname;Adress;product1;product2;product3;product4;";
int i = 0;
int index= 0;
while(i < 4){
index = csvContent.indexOf(';', (index + 1));
i++;
}//index = position of the thrid semicolon
Second, cut your CSV content at the index position.
String tmp1 = csvContent.substring(0, index);
String tmp2 = csvContent.substring(index, csvContent.length());
Thrid, replace all ';' by '::':
tmp2 = tmp2.replaceAll(";", "::");
Finaly, rebuild your file content:
csvContent = tmp1 + tmp2;
int i = 0;
int pos = 0;
while (i < 3) {
pos = string.indexOf(';', pos+1);
i++;
}
String newString = string.substring(0, pos) +";"+ (string.substring(pos + 1, string.length()).replace(";", "::"));
how about a regex solution?
Pattern pattern = Pattern.compile("(.*?;.*?;.*?;)(.*)");
Matcher match = pattern.matcher(str);
if(match.matches()) {
String firstThree = match.group(1);
String rest = match.group(2);
rest = rest.replace(";", "::");
return firstThree + rest;
}

detect incomplete patterns in strings

i have a string containing nested repeating patterns, for example:
String pattern1 = "1234";
String pattern2 = "5678";
String patternscombined = "1234|1234|5678|9"//added | for reading pleasure
String pattern = (pattern1 + pattern1 + pattern2 + "9")
+(pattern1 + pattern1 + pattern2 + "9")
+(pattern1 + pattern1 + pattern2 + "9")
String result = "1234|1234|5678|9|1234|1234|56";
As you can see in the above example, the result got cut off. But when knowing the repeating patterns, you can predict, what could come next.
Now to my question:
How can i predict the next repetitions of this pattern, to get a resulting string like:
String predictedresult = "1234|1234|5678|9|1234|1234|5678|9|1234|1234|5678|9";
Patterns will be smaller that 10 characters, the predicted result will be smaller than 1000 characters.
I am only receiving the cutoff result string and a pattern recognition program is already implemented and working. In the above example, i would have result, pattern1, pattern2 and patternscombined.
EDIT:
I have found a solution working for me:
import java.util.Arrays;
public class LRS {
// return the longest common prefix of s and t
public static String lcp(String s, String t) {
int n = Math.min(s.length(), t.length());
for (int i = 0; i < n; i++) {
if (s.charAt(i) != t.charAt(i))
return s.substring(0, i);
}
return s.substring(0, n);
}
// return the longest repeated string in s
public static String lrs(String s) {
// form the N suffixes
int N = s.length();
String[] suffixes = new String[N];
for (int i = 0; i < N; i++) {
suffixes[i] = s.substring(i, N);
}
// sort them
Arrays.sort(suffixes);
// find longest repeated substring by comparing adjacent sorted suffixes
String lrs = "";
for (int i = 0; i < N - 1; i++) {
String x = lcp(suffixes[i], suffixes[i + 1]);
if (x.length() > lrs.length())
lrs = x;
}
return lrs;
}
public static int startingRepeats(final String haystack, final String needle)
{
String s = haystack;
final int len = needle.length();
if(len == 0){
return 0;
}
int count = 0;
while (s.startsWith(needle)) {
count++;
s = s.substring(len);
}
return count;
}
public static String lrscutoff(String s){
String lrs = s;
int length = s.length();
for (int i = length; i > 0; i--) {
String x = lrs(s.substring(0, i));
if (startingRepeats(s, x) < 10 &&
startingRepeats(s, x) > startingRepeats(s, lrs)){
lrs = x;
}
}
return lrs;
}
// read in text, replacing all consecutive whitespace with a single space
// then compute longest repeated substring
public static void main(String[] args) {
long time = System.nanoTime();
long timemilis = System.currentTimeMillis();
String s = "12341234567891234123456789123412345";
String repeat = s;
while(repeat.length() > 0){
System.out.println("-------------------------");
String repeat2 = lrscutoff(repeat);
System.out.println("'" + repeat + "'");
int count = startingRepeats(repeat, repeat2);
String rest = repeat.substring(count*repeat2.length());
System.out.println("predicted: (rest ='" + rest + "')" );
while(count > 0){
System.out.print("'" + repeat2 + "' + ");
count--;
}
if(repeat.equals(repeat2)){
System.out.println("''");
break;
}
if(rest!="" && repeat2.contains(rest)){
System.out.println("'" + repeat2 + "'");
}else{
System.out.println("'" + rest + "'");
}
repeat = repeat2;
}
System.out.println("Time: (nano+millis):");
System.out.println(System.nanoTime()-time);
System.out.println(System.currentTimeMillis()-timemilis);
}
}
If your predict String is always piped(|) the numbers then you can easily split them using pipe and then keep track of the counts on a HashMap. For example
1234 = 2
1344 = 1
4411 = 5
But if not, then you have to modify the Longest Repeated Substring algorithm. As you need to have all repeated substrings so keep track of all instead of only the Longest one. Also, you have to put a checking for minimum length of substring along with overlapping substring. By searching google you'll find lot of reference of this algorithm.
You seem to need something like an n-gram language model, which is a statistical model that is based on counts of co-occurring events. If you are given some training data, you can derive the probabilities from counts of seen patterns. If not, you can try to specify them manually, but this can get tricky. Once you have such a language model (where the digit patterns correspond to words), you can always predict the next word by picking one with the highest probability given some previous words ("history").

Returning added string to

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

Adding dots into a system.out.println

well basically I have a line thats has a finial in of 40,
if a add 2 words that are 10 letters each long that leaves me with 20 spaces
thouse 20 spaces should be filled with dots ...... for example
hello................................you
or
stack...........................overflow
now I have the code working fine, I just have no idea on how to please this in a system.out.println so that the result is printed to the console.
I was thinking of using a whileloop to print the dots one at a time, after testing how many number of dots there should be once bother words are entered.
At the moment I have this which clearly doesn't work
{
System.out.println (word1 + ".." + word2);
while (wordlegnth1 + wordlegnth2 < LINELENGTH)
{
System.out.println (word1 + "." + word2);
wordlegnth1++;
}
final int LINE_LENGTH = 40;
String word1 = ...;
String word2 = ...;
StringBuilder sb = new StringBuilder(LINE_LENGTH);
sb.append(word1);
for (int i = 0; i + word1.length() + word2.length() < LINE_LENGTH; i++) {
sb.append(".");
}
sb.append(word2);
System.out.println(sb.toString());
Note: the use of StringBuilder is to avoid performance penalty due to String immutability
/**
* Formats a given string to 40 characters by prefixing it with word1 and
* suffixing it with word2 and using dots in the middle to make length of final
* string = 40.
* If string will not fit in 40 characters, -1 is returned, otherwise 0 is
* returned.
*
* #param word1 the first word
* #param word2 the second word
* #return 0 if string will fit in 40 characters, -1 otherwise
*/
public static int formatString(String word1, String word2) {
final int LINELENGTH = 40;
// determine how many dots we need
int diff = LINELENGTH - (word1.length() + word2.length());
if (diff < 0) {
// it's too big
System.out.println("string is too big to fit");
return -1;
}
// add the dots
StringBuilder dots = new StringBuilder();
for (int i = 0; i < diff; ++i) {
dots.append(".");
}
// build the final result
String result = word1 + dots.toString() + word2;
System.out.println(result);
return 0;
}
public static void main(String[] args) throws Throwable {
formatString("stack", "overflow");
String str = "";
for (int i = 0; i < 21; ++i) {
str += "a";
}
formatString(str, str);
}
Output:
stack...........................overflow
string is too big to fit
Why not start with something like:
int lengthA = word1.length();
int lengthB = word2.length();
int required = LINE_LENGHT - (lengthA + lengthB);
for(int i = 0; i < required; i++)
{
System.out.print(".");
}
It can be improved on (try strings that are very large for example, or use a StringBuilder rather than System.out.print).
add apache's commons-lang to your classpath and use StringUtils.leftPad()
System.out.println(str1 + StringUtils.leftPad(str2, 40 - str1.length(), '.'));
why write a method to do something someone else has already written + tested?

Categories

Resources