I want a user to put in a sentence with the scanner class.
Make sure to filter out all the spaces (for example the sentence: this is a test becomes thisisatest)
And then print out that sentence with a for loop with a space every 5 characters
(for example thisi sates t).
This is what i have so far
import java.util.Scanner;
public class BlockText {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Give your sentence: ");
String sentence = s.nextLine();
String nospace = sentence.replace(" ", "");
String out = "";
for (int i = 5; i < nospace.length(); i+=5) {
out += nospace.replaceAll( nospace.substring(i) , " ");
}
System.out.println("Sentence without spaces: " + nospace);
System.out.println("This gives: " + out);
}
}
but I have the issue that he repeats certain characters and removes others.
Like you can see underneath after "this gives:"
run:
Give your sentence:
this is a test
Sentence without spaces: thisisatest
This gives: thisi hisisa es
BUILD SUCCESSFUL (total time: 8 seconds)
Can someone help me out? Like I said in the titel, I want to accomplish this with a for loop and without using regex or StringBuilder.
You should really use a StringBuilder here, because appending strings with += in a loop is very inefficient.
Without a string builder, you can do something like this:
private static String addSpacesEvery5(String s) {
String out = "";
for (int i = 0 ; i < s.length() ; i++) {
if (i % 5 == 0 && i != 0) {
out += " "; // this will run once every five iterations, except the first one
}
out += s.charAt(i);
}
return out;
}
Or more efficiently without +=:
private static String addSpacesEvery5(String s) {
// s.length() / 5 is how many spaces we will add
char[] charArray = new char[s.length() + s.length() / 5];
int currentPos = 0;
for (int i = 0 ; i < s.length() ; i++) {
if (i % 5 == 0 && i != 0) {
charArray[currentPos] = ' ';
currentPos++;
}
charArray[currentPos] = s.charAt(i);
currentPos++;
}
return new String(charArray);
}
And then you can use it in your main method like this:
Scanner s = new Scanner(System.in);
System.out.println("Give your sentence: ");
String sentence = s.nextLine();
String nospace = sentence.replace(" ", "");
String out = addSpacesEvery5(nospace);
System.out.println("Sentence without spaces: " + nospace);
System.out.println("This gives: " + out);
With a string builder, the addSpacesEvery5 could be rewritten as:
private static String addSpacesEvery5(String s) {
StringBuilder out = new StringBuilder();
for (int i = 0 ; i < s.length() ; i++) {
if (i % 5 == 0 && i != 0) {
out.append(" ");
}
out.append(s.charAt(i));
}
return out.toString();
}
Here is one relatively simple and faster:
String tmp = new String();
int len = str.length();
int remOdds = len % 5;
int i = 0;
while (i < len - remOdds)
{
tmp = tmp.concat(str.substring(i, i + 5));
i += 5;
if (i < len)
{
tmp += " ";
}
}
while (i < len)
{
tmp += str.charAt(i);
i++;
}
str = tmp;
Related
I have a string of a random address like
String s = "H.N.-13/1443 laal street near bharath dental lab near thana qutubsher near modern bakery saharanpur uttar pradesh 247001";
I want to split it into array of string with two conditions:
each element of that array of string is of length less than or equal to 20
No awkward ending of an element of array of string
For example, splitting every 20 characters would produce:
"H.N.-13/1443 laal st"
"reet near bharath de"
"ntal lab near thana"
"qutubsher near moder"
"n bakery saharanpur"
but the correct output would be:
"H.N.-13/1443 laal"
"street near bharath"
"dental lab near"
"thana qutubsher near"
"modern bakery"
"saharanpur"
Notice how each element in string array is less than or equal to 20.
The above is my output for this code:
static String[] split(String s,int max){
int total_lines = s.length () / 24;
if (s.length () % 24 != 0) {
total_lines++;
}
String[] ans = new String[total_lines];
int count = 0;
int j = 0;
for (int i = 0; i < total_lines; i++) {
for (j = 0; j < 20; j++) {
if (ans[count] == null) {
ans[count] = "";
}
if (count > 0) {
if ((20 * count) + j < s.length()) {
ans[count] += s.charAt (20 * count + j);
} else {
break;
}
} else {
ans[count] += s.charAt (j);
}
}
String a = "";
a += ans[count].charAt (0);
if (a.equals (" ")) {
ans[i] = ans[i].substring (0, 0) + "" + ans[i].substring (1);
}
System.out.println (ans[i]);
count++;
}
return ans;
}
public static void main (String[]args) {
String add = "H.N.-13/1663 laal street near bharath dental lab near thana qutubsher near modern bakery";
String city = "saharanpur";
String state = "uttar pradesh";
String zip = "247001";
String s = add + " " + city + " " + state + " " + zip;
String[]ans = split (s);
}
Find all occurrences of up to 20 chars starting with a non-space and ending with a word boundary, and collect them to a List:
List<String> parts = Pattern.compile("\\S.{1,19}\\b").matcher(s)
.results()
.map(MatchResult::group)
.collect(Collectors.toList());
See live demo.
The code is not very clear, but at first glance it seems you are building character by character that is why you are getting the output you see. Instead you go word by word if you want to retain a word and overflow it to next String if necessary. A more promising code would be:
static String[] splitString(String s, int max) {
String[] words = s.split("\s+");
List<String> out = new ArrayList<>();
int numWords = words.length;
int i = 0;
while (i <numWords) {
int len = 0;
StringBuilder sb = new StringBuilder();
while (i < numWords && len < max) {
int wordLength = words[i].length();
len += (i == numWords-1 ? wordLength : wordLength + 1);//1 for space
if (len <= max) {
sb.append(words[i]+ " ");
i++;
}
}
out.add(sb.toString().trim());
}
return out.toArray(new String[] {});
}
Note: It works on your example input, but you may need to tweak it so it works for cases like a long word containing more than 20 characters, etc.
I need to write encryptor.
The first letter needs to be converted to its ASCII code.
The second letter needs to be switched with the last letter
It should make from this "65 119esi 111dl 111lw 108dvei 105n 97n 111ka"
This "A wise old owl lived in an oak"
But when I check is char a digit i have an error.
public static void encryptThis(String text) {
String [] text_arr = text.split("\\s");
for(int i = 0; i < text_arr.length;i++){
String word = text_arr[i];
int length = word.length();
char [] char_word = new char [length];
char_word = word.toCharArray();
int k = 0;
length = char_word.length;
char [] numb = new char [length];
for (int j = 0;j < length; j++){
//In this place
if (Character.isDigit(char_word[i])){
numb[k] = char_word[i];
char_word[i] = ' ';
k++;
System.out.println(numb[k]);
}
}
int number = Integer.parseInt(numb.toString(),8);
String edit_char_word = char_word.toString();
String final_str = new StringBuffer(edit_char_word).reverse().toString().trim();
final_str = number + final_str;
text_arr[i] = final_str;
}
String fin_text = text_arr.toString();
return fin_text;
}
I wrote the encryptThis method from the beginning and tried to stay as simple to get readable code, here is
a full working example. As you did not mention what to do if a number is the first "letter" there is no
special handling for this - an "1" will be encrypted to (ascii) 49...
public class Main_So {
public static void main(String[] args) {
System.out.println("https://stackoverflow.com/questions/62460366/in-function-isdigit-java-lang-arrayindexoutofboundsexception-5");
String input = "A wise old owl lived in an oak";
String outputExpected = "65 119esi 111dl 111lw 108dvei 105n 97n 111ka";
System.out.println("input: " + input);
String output = encryptThis(input);
System.out.println("output encryptThis: " + output);
System.out.println("output expected: " + outputExpected);
}
public static String encryptThis(String text) {
String[] text_arr = text.split("\\s");
String outputString = "";
for (int i = 0; i < text_arr.length; i++) {
String word = text_arr[i];
int length = word.length();
int ascii0 = (int) word.charAt(0);
String word0 = String.valueOf(ascii0); // first char as ascii code
String subString = word.substring(1);
if (length > 2) { // switch chars if string length > 2
char char1 = word.charAt(1);
char charEnd = word.charAt(length - 1);
StringBuilder wordBuilder = new StringBuilder(subString);
wordBuilder.setCharAt(0, charEnd);
wordBuilder.setCharAt((length - 2), char1);
subString = String.valueOf(wordBuilder);
}
outputString = outputString + word0 + subString + " ";
}
return outputString;
}
}
This is the result:
input: A wise old owl lived in an oak
output encryptThis: 65 119esi 111dl 111lw 108dvei 105n 97n 111ka
output expected: 65 119esi 111dl 111lw 108dvei 105n 97n 111ka
I have somehow got the output with the help of some browsing. But I couldn't understand the logic behind the code. Is there any simple way to achieve this?
public class LetterCount {
public static void main(String[] args)
{
String str = "aabbcccddd";
int[] counts = new int[(int) Character.MAX_VALUE];
// If you are certain you will only have ASCII characters, I would use `new int[256]` instead
for (int i = 0; i < str.length(); i++) {
char charAt = str.charAt(i);
counts[(int) charAt]++;
}
for (int i = 0; i < counts.length; i++) {
if (counts[i] > 0)
//System.out.println("Number of " + (char) i + ": " + counts[i]);
System.out.print(""+ counts[i] + (char) i + "");
}
}
}
There are 3 conditions which need to be taken care of:
if (s.charAt(x) != s.charAt(x + 1) && count == 1) ⇒ print the counter and character;
if (s.charAt(x) == s.charAt(x + 1)) ⇒ increase the counter;
if (s.charAt(x) != s.charAt(x + 1) && count >= 2) ⇒ reset to counter 1.
{
int count= 1;
int x;
for (x = 0; x < s.length() - 1; x++) {
if (s.charAt(x) != s.charAt(x + 1) && count == 1) {
System.out.print(s.charAt(x));
System.out.print(count);
}
else if (s.charAt(x)== s.charAt(x + 1)) {
count++;
}
else if (s.charAt(x) != s.charAt(x + 1) && count >= 2) {
System.out.print(s.charAt(x));
System.out.print(count);
count = 1;
}
}
System.out.print(s.charAt(x));
System.out.println(count);
}
The code is really simple.It uses the ASCII value of a character to index into the array that stores the frequency of each character.
The output is simply got by iterating over that array and which character has frequency greater than 1, print it accordingly as you want in the output that is frequency followed by character.
If the input string has same characters consecutive then the solution can be using space of O(1)
For example in your string aabbcc, the same characters are consecutive , so we can take advantage of this fact and count the character frequency and print it at the same time.
for (int i = 0; i < str.length(); i++)
{
int freq = 1;
while((i+1)<str.length()&&str.charAt(i) == str.charAt(i+1))
{++freq;++i}
System.out.print(freq+str.charAt(i));
}
You are trying to keep count of the number of times each character is found. An array is referenced by an index. For example, the ASCII code for the lowercase letter a is the integer 97. Thus the count of the number of times the letter a is seen is in counts[97]. After every element in the counts array has been set, you print out how many have been found.
This should help you understand the basic idea behind how to approach the string compression problem
import java.util.*;
public class LetterCount {
public static void main(String[] args) {
//your input string
String str = "aabbcccddd";
//split your input into characters
String chars[] = str.split("");
//maintain a map to store unique character and its frequency
Map<String, Integer> compressMap = new LinkedHashMap<String, Integer>();
//read every letter in input string
for(String s: chars) {
//java.lang.String.split(String) method includes empty string in your
//split array, so you need to ignore that
if("".equals(s))
continue;
//obtain the previous occurances of the character
Integer count = compressMap.get(s);
//if the character was previously encountered, increment its count
if(count != null)
compressMap.put(s, ++count);
else//otherwise store it as first occurance
compressMap.put(s, 1);
}
//Create a StringBuffer object, to append your input
//StringBuffer is thread safe, so I prefer using it
//you could use StringBuilder if you don't expect your code to run
//in a multithreaded environment
StringBuffer output = new StringBuffer("");
//iterate over every entry in map
for (Map.Entry<String, Integer> entry : compressMap.entrySet()) {
//append the results to output
output.append(entry.getValue()).append(entry.getKey());
}
//print the output on console
System.out.println(output);
}
}
class Solution {
public String toFormat(String input) {
char inChar[] = input.toCharArray();
String output = "";
int i;
for(i=0;i<input.length();i++) {
int count = 1;
while(i+1<input.length() && inChar[i] == inChar[i+1]) {
count+=1;
i+=1;
}
output+=inChar[i]+String.valueOf(count);
}
return output;
}
public static void main(String[] args) {
Solution sol = new Solution();
String input = "aaabbbbcc";
System.out.println("Formatted String is: " + sol.toFormat(input));
}
}
def encode(Test_string):
count = 0
Result = ""
for i in range(len(Test_string)):
if (i+1) < len(Test_string) and (Test_string[i] == Test_string[i+1]):
count += 1
else:
Result += str((count+1))+Test_string[i]
count = 0
return Result
print(encode("ABBBBCCCCCCCCAB"))
If you want to get the correct count considering the string is not in alphabetical order. Sort the string
public class SquareStrings {
public static void main(String[] args) {
SquareStrings squareStrings = new SquareStrings();
String str = "abbccddddbd";
System.out.println(squareStrings.manipulate(str));
}
private String manipulate(String str1) {
//convert to charArray
char[] charArray = str1.toCharArray();
Arrays.sort(charArray);
String str = new String(charArray);
StringBuilder stbuBuilder = new StringBuilder("");
int length = str.length();
String temp = "";
if (length > 1) {
for (int i = 0; i < length; i++) {
int freq = 1;
while (((i + 1) < length) && (str.charAt(i) == str.charAt(i + 1))) {
++freq;
temp = str.charAt(i) + "" + freq;
++i;
}
stbuBuilder.append(temp);
}
} else {
return str + "" + 1;
}
return stbuBuilder.toString();
}
}
Kotlin:
fun compressString(input: String): String {
if (input.isEmpty()){
return ""
}
var result = ""
var count = 1
var char1 = input[0]
for (i in 1 until input.length) {
val char2 = input[i]
if (char1 == char2) {
count++
} else {
if (count != 1) {
result += "$count$char1"
count = 1
} else {
result += "$char1"
}
char1 = char2
}
}
result += if (count != 1) {
"$count$char1"
} else {
"$char1"
}
return result
}
I have a small program that is suppose to count lexemes. What I get is an erorr. I am fairly new to Java. Here is the error:
c:\programming>java LexemesTokenizer
input string: a = ++b; c = d - /* -e */ --f;
Input string= a
Delimeter= .
Total number of delimeters= 2 Exception in thread "main" java.lang.StringIndexOu
tOfBoundsException: String index out of range: 2
at java.lang.String.charAt(Unknown Source)
at LexemesTokenizer.main(LexemesTokenizer.java:41)
Here is the code
import java.util.Scanner;
public class LexemesTokenizer {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("input string: ");
String s = scanner.next();
char delimiter = ' ';
// calculate number of delimiter characters
int cntDelimeters = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != delimiter) {
cntDelimeters++;
}
}
System.out.println("Input string= " + s);
System.out.printf("Delimeter= %c.", delimiter);
System.out.printf("\nTotal number of delimeters= %d ", cntDelimeters);
String[] lexemes = new String[cntDelimeters + 1];
// parse cntDelimeters+1 lexemes and store in an array
int right = 0;
int left = 0;
for (int i = 0; i < cntDelimeters; i++) {
while (s.charAt(right) != delimiter) {
right++;
}
lexemes[i] = s.substring(left, right);
right++;
left = right;
}
lexemes[cntDelimeters] = s.substring(right, s.length());
// print results for testing
for (int i = 0; i < lexemes.length; i++) {
System.out.printf("\n%d : %s", i, lexemes[i]);
}
}
}
line 41 is:while (s.charAt(right) != delimiter) {
Some reason string s is only an a
any help is much appreciated.
Well, your string (s) doesn't contain a space (the value you assigned to delimiter) and then you do this:
while (s.charAt(right) != delimiter) {
right++;
}
Thus flying right off the end of your string.
while (s.charAt(right) != delimiter && s.length > right)
Will take care of that.
Edit: To help with the input string
You're calling
String s = scanner.next();
Scanner is an input parser. This is going to return the first word of your input ("a") because the default delimiter is whitespace.
If you want the entire line of input you use:
String s = scanner.nextLine();
I have a StringBuilder object that needs to be trimmed (i.e. all whitespace chars /u0020 and below removed from either end).
I can't seem to find a method in string builder that would do this.
Here's what I'm doing now:
String trimmedStr = strBuilder.toString().trim();
This gives exactly the desired output, but it requires two Strings to be allocated instead of one. Is there a more efficient to trim the string while it's still in the StringBuilder?
You should not use the deleteCharAt approach.
As Boris pointed out, the deleteCharAt method copies the array over every time. The code in the Java 5 that does this looks like this:
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
Of course, speculation alone is not enough to choose one method of optimization over another, so I decided to time the 3 approaches in this thread: the original, the delete approach, and the substring approach.
Here is the code I tested for the orignal:
public static String trimOriginal(StringBuilder sb) {
return sb.toString().trim();
}
The delete approach:
public static String trimDelete(StringBuilder sb) {
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
sb.deleteCharAt(0);
}
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
And the substring approach:
public static String trimSubstring(StringBuilder sb) {
int first, last;
for (first=0; first<sb.length(); first++)
if (!Character.isWhitespace(sb.charAt(first)))
break;
for (last=sb.length(); last>first; last--)
if (!Character.isWhitespace(sb.charAt(last-1)))
break;
return sb.substring(first, last);
}
I performed 100 tests, each time generating a million-character StringBuffer with ten thousand trailing and leading spaces. The testing itself is very basic, but it gives a good idea of how long the methods take.
Here is the code to time the 3 approaches:
public static void main(String[] args) {
long originalTime = 0;
long deleteTime = 0;
long substringTime = 0;
for (int i=0; i<100; i++) {
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
StringBuilder sb3 = new StringBuilder();
for (int j=0; j<10000; j++) {
sb1.append(" ");
sb2.append(" ");
sb3.append(" ");
}
for (int j=0; j<980000; j++) {
sb1.append("a");
sb2.append("a");
sb3.append("a");
}
for (int j=0; j<10000; j++) {
sb1.append(" ");
sb2.append(" ");
sb3.append(" ");
}
long timer1 = System.currentTimeMillis();
trimOriginal(sb1);
originalTime += System.currentTimeMillis() - timer1;
long timer2 = System.currentTimeMillis();
trimDelete(sb2);
deleteTime += System.currentTimeMillis() - timer2;
long timer3 = System.currentTimeMillis();
trimSubstring(sb3);
substringTime += System.currentTimeMillis() - timer3;
}
System.out.println("original: " + originalTime + " ms");
System.out.println("delete: " + deleteTime + " ms");
System.out.println("substring: " + substringTime + " ms");
}
I got the following output:
original: 176 ms
delete: 179242 ms
substring: 154 ms
As we see, the substring approach provides a very slight optimization over the original "two String" approach. However, the delete approach is extremely slow and should be avoided.
So to answer your question: you are fine trimming your StringBuilder the way you suggested in the question. The very slight optimization that the substring method offers probably does not justify the excess code.
I've used Zaven's analysis approach and StringBuilder's delete(start, end) method which performs far better than the deleteCharAt(index) approach, but slightly worse than the substring() approach. This method also uses the array copy, but array copy is called far fewer times (only twice in the worst case). In addition, this avoids creating multiple instances of intermediate Strings in case trim() is called repeatedly on the same StringBuilder object.
public class Main {
public static String trimOriginal(StringBuilder sb) {
return sb.toString().trim();
}
public static String trimDeleteRange(StringBuilder sb) {
int first, last;
for (first = 0; first < sb.length(); first++)
if (!Character.isWhitespace(sb.charAt(first)))
break;
for (last = sb.length(); last > first; last--)
if (!Character.isWhitespace(sb.charAt(last - 1)))
break;
if (first == last) {
sb.delete(0, sb.length());
} else {
if (last < sb.length()) {
sb.delete(last, sb.length());
}
if (first > 0) {
sb.delete(0, first);
}
}
return sb.toString();
}
public static String trimSubstring(StringBuilder sb) {
int first, last;
for (first = 0; first < sb.length(); first++)
if (!Character.isWhitespace(sb.charAt(first)))
break;
for (last = sb.length(); last > first; last--)
if (!Character.isWhitespace(sb.charAt(last - 1)))
break;
return sb.substring(first, last);
}
public static void main(String[] args) {
runAnalysis(1000);
runAnalysis(10000);
runAnalysis(100000);
runAnalysis(200000);
runAnalysis(500000);
runAnalysis(1000000);
}
private static void runAnalysis(int stringLength) {
System.out.println("Main:runAnalysis(string-length=" + stringLength + ")");
long originalTime = 0;
long deleteTime = 0;
long substringTime = 0;
for (int i = 0; i < 200; i++) {
StringBuilder temp = new StringBuilder();
char[] options = {' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd'};
for (int j = 0; j < stringLength; j++) {
temp.append(options[(int) ((Math.random() * 1000)) % options.length]);
}
String testStr = temp.toString();
StringBuilder sb1 = new StringBuilder(testStr);
StringBuilder sb2 = new StringBuilder(testStr);
StringBuilder sb3 = new StringBuilder(testStr);
long timer1 = System.currentTimeMillis();
trimOriginal(sb1);
originalTime += System.currentTimeMillis() - timer1;
long timer2 = System.currentTimeMillis();
trimDeleteRange(sb2);
deleteTime += System.currentTimeMillis() - timer2;
long timer3 = System.currentTimeMillis();
trimSubstring(sb3);
substringTime += System.currentTimeMillis() - timer3;
}
System.out.println(" original: " + originalTime + " ms");
System.out.println(" delete-range: " + deleteTime + " ms");
System.out.println(" substring: " + substringTime + " ms");
}
}
Output:
Main:runAnalysis(string-length=1000)
original: 0 ms
delete-range: 4 ms
substring: 0 ms
Main:runAnalysis(string-length=10000)
original: 4 ms
delete-range: 9 ms
substring: 4 ms
Main:runAnalysis(string-length=100000)
original: 22 ms
delete-range: 33 ms
substring: 43 ms
Main:runAnalysis(string-length=200000)
original: 57 ms
delete-range: 93 ms
substring: 110 ms
Main:runAnalysis(string-length=500000)
original: 266 ms
delete-range: 220 ms
substring: 191 ms
Main:runAnalysis(string-length=1000000)
original: 479 ms
delete-range: 467 ms
substring: 426 ms
Don't worry about having two strings. It's a microoptimization.
If you really have detected a bottleneck, you can have a nearly-constant-time trimming - just iterate the first N chars, until they are Character.isWhitespace(c)
only one of you have taken into account that when you convert the String builder to a "string" and then "trim" that you create an immutable object twice that has to be garbage collected,
so the total allocation is:
Stringbuilder object
immutable string of the SB object
1 immutable object of the string that has been trimmed.
So whilst it may "appear" that the trim is faster, in the real world and with a loaded memory scheme it will in fact be worse.
I had exactly your question at first, however, after 5-minute's second thought, I realized actually you never need to trim the StringBuffer! You only need to trim the string you append into the StringBuffer.
If you want to trim an initial StringBuffer, you can do this:
StringBuffer sb = new StringBuffer(initialStr.trim());
If you want to trim StringBuffer on-the-fly, you can do this during append:
Sb.append(addOnStr.trim());
You get two strings, but I'd expect the data to be only allocated once. Since Strings in Java are immutable, I'd expect the trim implementation to give you an object that shares the same character data, but with different start- and end indices. At least that's what the substr method does. So, anything you try to optimise this most certainly will have the opposite effect, since you add overhead that is not needed.
Just step through the trim() method with your debugger.
I made some code. It works and the test cases are there for you to see. Let me know if this is okay.
Main code -
public static StringBuilder trimStringBuilderSpaces(StringBuilder sb) {
int len = sb.length();
if (len > 0) {
int start = 0;
int end = 1;
char space = ' ';
int i = 0;
// Remove spaces at start
for (i = 0; i < len; i++) {
if (sb.charAt(i) != space) {
break;
}
}
end = i;
//System.out.println("s = " + start + ", e = " + end);
sb.delete(start, end);
// Remove the ending spaces
len = sb.length();
if (len > 1) {
for (i = len - 1; i > 0; i--) {
if (sb.charAt(i) != space) {
i = i + 1;
break;
}
}
start = i;
end = len;// or len + any positive number !
//System.out.println("s = " + start + ", e = " + end);
sb.delete(start, end);
}
}
return sb;
}
The full code with test -
package source;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
public class StringBuilderTrim {
public static void main(String[] args) {
testCode();
}
public static void testCode() {
StringBuilder s1 = new StringBuilder("");
StringBuilder s2 = new StringBuilder(" ");
StringBuilder s3 = new StringBuilder(" ");
StringBuilder s4 = new StringBuilder(" 123");
StringBuilder s5 = new StringBuilder(" 123");
StringBuilder s6 = new StringBuilder("1");
StringBuilder s7 = new StringBuilder("123 ");
StringBuilder s8 = new StringBuilder("123 ");
StringBuilder s9 = new StringBuilder(" 123 ");
StringBuilder s10 = new StringBuilder(" 123 ");
/*
* Using a rough form of TDD here. Initially, one one test input
* "test case" was added and rest were commented. Write no code for the
* method being tested. So, the test will fail. Write just enough code
* to make it pass. Then, enable the next test. Repeat !!!
*/
ArrayList<StringBuilder> ins = new ArrayList<StringBuilder>();
ins.add(s1);
ins.add(s2);
ins.add(s3);
ins.add(s4);
ins.add(s5);
ins.add(s6);
ins.add(s7);
ins.add(s8);
ins.add(s9);
ins.add(s10);
// Run test
for (StringBuilder sb : ins) {
System.out
.println("\n\n---------------------------------------------");
String expected = sb.toString().trim();
String result = trimStringBuilderSpaces(sb).toString();
System.out.println("In [" + sb + "]" + ", Expected [" + expected
+ "]" + ", Out [" + result + "]");
if (result.equals(expected)) {
System.out.println("Success!");
} else {
System.out.println("FAILED!");
}
System.out.println("---------------------------------------------");
}
}
public static StringBuilder trimStringBuilderSpaces(StringBuilder inputSb) {
StringBuilder sb = new StringBuilder(inputSb);
int len = sb.length();
if (len > 0) {
try {
int start = 0;
int end = 1;
char space = ' ';
int i = 0;
// Remove spaces at start
for (i = 0; i < len; i++) {
if (sb.charAt(i) != space) {
break;
}
}
end = i;
//System.out.println("s = " + start + ", e = " + end);
sb.delete(start, end);
// Remove the ending spaces
len = sb.length();
if (len > 1) {
for (i = len - 1; i > 0; i--) {
if (sb.charAt(i) != space) {
i = i + 1;
break;
}
}
start = i;
end = len;// or len + any positive number !
//System.out.println("s = " + start + ", e = " + end);
sb.delete(start, end);
}
} catch (Exception ex) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
sw.toString(); // stack trace as a string
sb = new StringBuilder("\nNo Out due to error:\n" + "\n" + sw);
return sb;
}
}
return sb;
}
}
strBuilder.replace(0,strBuilder.length(),strBuilder.toString().trim());
Since deleteCharAt() copies array each time, I have come with below code which copies array two times in worst case when StringBuilder have both leading and trailing whitespace. Below code will make sure that object reference remains same and we are not creating new object.
public static void trimStringBuilder(StringBuilder builder) {
int len = builder.length();
int start = 0;
// Remove whitespace from start
while (start < len && builder.charAt(start) == ' ') {
start++;
}
if (start > 0) {
builder.delete(0, start);
}
len = builder.length();
int end = len;
// Remove whitespace from end
while (end > 0 && builder.charAt(end - 1) == ' ') {
end--;
}
if (end < len) {
builder.delete(end, len);
}
}