Elegant Solutions to the Fencepost Problem (with Strings) - java

What I'm referring to is concatenating Strings with a certain String in the middle, such as concatenating sentences separated by a period, or parameter lists with a comma. I know you can use libraries, but sometimes these can't do what you want, like when you want to generate the phrases you are concatenating. So far I've come up with two solutions,
StringBuffer sentence = new StringBuffer();
String period = "";
for ( int i = 0; i < sentences.length; i++ ) {
sentence.append( period + sentences[i] );
period = ". ";
}
which suffers from the redundant reassignment of period. There is also
StringBuffer actualParameters = new StringBuffer();
actualParameters.append( parameters[0] );
for ( int i = 1; i < parameters.length; i++ ) {
actualParameters.append( ", " + parameters[i] );
}
which removes the reassignment but which still looks unappealing. Any other solutions are greatly appreciated.

There is a family of functions in Apache Commons Lang that does just that.
If you have to code it yourself, the way I usually do this sort of thing is as follows:
StringBuilder sb = new StringBuilder();
for (String sentence : sentences) {
if (sb.length() != 0) {
sb.append(". ");
}
sb.append(sentence);
}
This version permits sentences to be any iterable (returning strings). Also note the use of StringBuilder instead of StringBuffer.
It is easy to generalize this to something akin to org.apache.commons.lang.StringUtils.join.

If you have at least one string then:
String join(String separator, String... strings)
{
String s = strings[0];
for (int i = 1; i < strings.length; i++) {
s += separator + strings[i];
}
return s;
}

Seems like a common question!
Remove last character of a StringBuilder?
That would lead to something like:
StringBuffer sentence = new StringBuffer();
String separator = ", ";
for ( int i = 0; i < sentences.length; i++ ) {
sentence.append( sentences[i] )
sentence.append( separator );
}
sentence.setLength(sentence.length() - separator.length());

public String join(String sep, String... parts) {
boolean first = true;
final StringBuilder sb = new StringBuilder();
for(String part: parts) {
if(first)
first = false;
else
sb.append(sep);
sb.append(part);
}
}
Don't use StringBuffer because of unnecessary synchronisation and "+" operator, because this will create unnecassry intemediate String objects.

Related

Censor word from a string [duplicate]

I want to replace all the characters in a Java String with * character. So it shouldn't matter what character it is, it should be replaced with a *.
I know there are heaps of examples there on internet but have not one that replaces every character and I have tried myself but no success.
Java 11 and later
str = "*".repeat(str.length());
Note: This replaces newlines \n with *. If you want to preserve \n, see solution below.
Java 10 and earlier
str = str.replaceAll(".", "*");
This preserves newlines.
To replace newlines with * as well in Java 10 and earlier, you can use:
str = str.replaceAll("(?s).", "*");
The (?s) doesn't match anything but activates DOTALL mode which makes . also match \n.
Don't use regex at all, count the String length, and return the according number of stars.
Plain Java < 8 Version:
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for(int i = =; i < len; i++){
sb.append('*');
}
return sb.toString();
Plain Java >= 8 Version:
int len = str.length();
return IntStream.range(0, n).mapToObj(i -> "*").collect(Collectors.joining());
Using Guava:
return Strings.repeat("*", str.length());
// OR
return CharMatcher.ANY.replaceFrom(str, '*');
Using Commons / Lang:
return StringUtils.repeat("*", str.length());
System.out.println("foobar".replaceAll(".", "*"));
public String allStar(String s) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
sb.append('*');
}
return sb.toString();
}
How abt creating a new string with the number of * = number of last string char?
StringBuffer bf = new StringBuffer();
for (int i = 0; i < source.length(); i++ ) {
bf.append('*');
}
There may be other faster/better ways to do it, but you could just use a string buffer and a for-loop:
public String stringToAsterisk(String input) {
if (input == null) return "";
StringBuffer sb = new StringBuffer();
for (int x = 0; x < input.length(); x++) {
sb.append("*");
}
return sb.toString();
}
If your application is single threaded, you can use StringBuilder instead, but it's not thread safe.
I am not sure if this might be any faster:
public String stringToAsterisk(String input) {
if (input == null) return "";
int length = input.length();
char[] chars = new char[length];
while (length > 0) chars[--length] = "*";
return new String(chars);
}
Without any external library and without your own loop, you can do:
String input = "Hello";
char[] ca = new char[input.length()];
Arrays.fill(ca, '*');
String output = new String(ca);
BTW, both Arrays.fill() and String(char []) are really fast.
Recursive method
String nCopies(String s, int n) {
return n == 1 ? s.replaceFirst(".$", "") : nCopies(s + s, --n);
}
String text = "Hello World";
System.out.println( text.replaceAll( "[A-Za-z0-9]", "*" ) );
output : ***** *****

Efficient and non-interfering way of replacing multiple substrings in a String

I'm trying to apply the same replacement instructions several thousand times to different input strings with as little overhead as possible. I need to consider two things for this:
The search Strings aren't necessarily all the same length: one may be just "a", another might be "ch", yet another might be "sch"
What was already replaced shall not be replaced again: If the replacement patterns are [a->e; e->a], "beat" should become "baet", not "baat" or "beet".
With that in mind, this is the code I came up with:
public class Replacements {
private String[] search;
private String[] replace;
Replacements(String[] s, String[] r)
{
if (s.length!=r.length) throw new IllegalArgumentException();
Map<String,String> map = new HashMap<String,String>();
for (int i=0;i<s.length;i++)
{
map.put(s[i], r[i]);
}
List<String> sortedKeys = new ArrayList(map.keySet());
Collections.sort(sortedKeys, new StringLengthComparator());
this.search = sortedKeys.toArray(new String[0]);
Stack<String> r2 = new Stack<>();
sortedKeys.stream().forEach((i) -> {
r2.push(map.get(i));
});
this.replace = r2.toArray(new String[0]);
}
public String replace(String input)
{
return replace(input,0);
}
private String replace(String input,int i)
{
String out = "";
List<String> parts = Arrays.asList(input.split(this.search[i],-1));
for (Iterator it = parts.iterator(); it.hasNext();)
{
String part = it.next().toString();
if (part.length()>0 && i<this.search.length-1) out += replace(part,i+1);
if (it.hasNext()) out += this.replace[i];
}
return out;
}
}
And then
String[] words;
//fill variable words
String[] s_input = "ou|u|c|ch|ce|ci".split("\\|",-1);
String[] r_input = "u|a|k|c|se|si".split("\\|",-1);
Replacements reps = new Replacements(s_input,r_input);
for (String word : words) {
System.out.println(reps.replace(word));
}
(s_input and r_input would be up to the user, so they're just examples, just like the program wouldn't actually use println())
This code makes sure longer search strings get looked for first and also covers the second condition above.
It is, however, quite costly. What would be the most efficient way to accomplish what I'm doing here (especially if the number of Strings in words is significantly large)?
With my current code, "couch" should be converted into "kuc" (except it doesn't, apparently; it now does, thanks to the -1 in split(p,-1))
This is not a full solution but it shows how to scan the input and find all target substrings in one pass. You would use a StringBuilder to assemble the result, looking up the replacements in a Map as you are currently doing. Use the start and end indexes to handle copying of non-matching segments.
public static void main(String[] args) throws Exception
{
Pattern p = Pattern.compile("(ou|ch|ce|ci|u|c)");
Matcher m = p.matcher("auouuchcceaecxici");
while (m.find())
{
MatchResult r = m.toMatchResult();
System.out.printf("s=%d e=%d '%s'\n", r.start(), r.end(), r.group());
}
}
Output:
s=1 e=2 'u'
s=2 e=4 'ou'
s=4 e=5 'u'
s=5 e=7 'ch'
s=7 e=8 'c'
s=8 e=10 'ce'
s=12 e=13 'c'
s=15 e=17 'ci'
Note the strings in the regex have to be sorted in order of descending length to work correctly.
One could make a regex pattern from the keys and leave it to that module for optimization.
Obviously
"(ou|u|ch|ce|ci|c)"
needs to take care of ce/ci/c, either by reverse sorting or immediately as tree:
"(c(e|h|i)?|ou|u)"
Then
String soughtKeys = "ou|u|ch|ce|ci|c"; // c last
String replacements = "u|a|c|se|si|k";
Map<String, String> map = new HashMap<>();
... fill map
Pattern pattern = Pattern.compile("(" + soughtKeys + ")");
for (String word : words) {
StringBuffer sb = new StringBuffer();
Matcher m = pattern.matcher(word);
while (m.find()) {
m.appendReplacement(sb, map.get(m.group());
}
m.appendTail(sb);
System.out.printf("%s -> %s%n", word, sb.toString());
}
The advantage being that regex is quite smart (though slow), and replacements are not done over replaced text.
public class Replacements
{
private String[] search; // sorted in descending length and order, eg: sch, ch, c
private String[] replace; // corresponding replacement
Replacements(String[] s, String[] r)
{
if (s.length != r.length)
throw new IllegalArgumentException();
final TreeMap<String, String> map = new TreeMap<String, String>(Collections.reverseOrder());
for (int i = 0; i < s.length; i++)
map.put(s[i], r[i]);
this.search = map.keySet().toArray(new String[map.size()]);
this.replace = map.values().toArray(new String[map.size()]);
}
public String replace(String input)
{
final StringBuilder result = new StringBuilder();
// start of yet-to-be-copied substring
int s = 0;
SEARCH:
for (int i = s; i < input.length(); i++)
{
for (int p = 0; p < this.search.length; p++)
{
if (input.regionMatches(i, this.search[p], 0, this.search[p].length()))
{
// append buffer and replacement
result.append(input, s, i).append(this.replace[p]);
// skip beyond current match and reset buffer
i += this.search[p].length();
s = i--;
continue SEARCH;
}
}
}
if (s == 0) // no matches? no changes!
return input;
// append remaining buffer
return result.append(input, s, input.length()).toString();
}
}

How to write a replaceAll function java?

I'm trying to write a program that will allow a user to input a phrase (for example: "I like cats") and print each word on a separate line. I have already written the part to allow a new line at every space but I don't want to have blank lines between the words because of excess spaces. I can't use any regular expressions such as String.split(), replaceAll() or trim().
I tried using a few different methods but I don't know how to delete spaces if you don't know the exact number there could be. I tried a bunch of different methods but nothing seems to work.
Is there a way I could implement it into the code I've already written?
for (i=0; i<length-1;) {
j = text.indexOf(" ", i);
if (j==-1) {
j = text.length();
}
System.out.print("\n"+text.substring(i,j));
i = j+1;
}
Or how can I write a new expression for it? Any suggestions would really be appreciated.
I have already written the part to allow a new line at every space but
I don't want to have blank lines between the words because of excess
spaces.
If you can't use trim() or replaceAll(), you can use java.util.Scanner to read each word as a token. By default Scanner uses white space pattern as a delimiter for finding tokens. Similarly, you can also use StringTokenizer to print each word on new line.
String str = "I like cats";
Scanner scanner = new Scanner(str);
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
OUTPUT
I
like
cats
Here is a simple solution using substring() and indexOf()
public static void main(String[] args) {
List<String> split = split("I like cats");
split.forEach(System.out::println);
}
public static List<String> split(String s){
List<String> list = new ArrayList<>();
while(s.contains(" ")){
int pos = s.indexOf(' ');
list.add(s.substring(0, pos));
s = s.substring(pos + 1);
}
list.add(s);
return list;
}
Edit:
If you only want to print the text without splitting or making lists, you can use this:
public static void main(String[] args) {
newLine("I like cats");
}
public static void newLine(String s){
while(s.contains(" ")){
int pos = s.indexOf(' ');
System.out.println(s.substring(0, pos));
s = s.substring(pos + 1);
}
System.out.println(s);
}
I think this will solve your problem.
public static List<String> getWords(String text) {
List<String> words = new ArrayList<>();
BreakIterator breakIterator = BreakIterator.getWordInstance();
breakIterator.setText(text);
int lastIndex = breakIterator.first();
while (BreakIterator.DONE != lastIndex) {
int firstIndex = lastIndex;
lastIndex = breakIterator.next();
if (lastIndex != BreakIterator.DONE && Character.isLetterOrDigit(text.charAt(firstIndex))) {
words.add(text.substring(firstIndex, lastIndex));
}
}
return words;
}
public static void main(String[] args) {
String text = "I like cats";
List<String> words = getWords(text);
for (String word : words) {
System.out.println(word);
}
}
Output :
I
like
cats
What about something like this, its O(N) time complexity:
Just use a string builder to create the string as you iterate through your string, add "\n" whenever you find a space
String word = "I like cats";
StringBuilder sb = new StringBuilder();
boolean newLine = true;
for(int i = 0; i < word.length(); i++) {
if (word.charAt(i) == ' ') {
if (newLine) {
sb.append("\n");
newLine = false;
}
} else {
newLine = true;
sb.append(word.charAt(i));
}
}
String result = sb.toString();
EDIT: Fixed the problem mentioned on comments (new line on multiple spaces)
Sorry, I didnot caution you cannot use replaceAll().
This is my other solution:
String s = "I like cats";
Pattern p = Pattern.compile("([\\S])+");
Matcher m = p.matcher(s);
while (m.find( )) {
System.out.println(m.group());
}
Old solution:
String s = "I like cats";
System.out.println(s.replaceAll("( )+","\n"));
You almost done all job. Just make small addition, and your code will work as you wish:
for (int i = 0; i < length - 1;) {
j = text.indexOf(" ", i);
if (i == j) { //if next space after space, skip it
i = j + 1;
continue;
}
if (j == -1) {
j = text.length();
}
System.out.print("\n" + text.substring(i, j));
i = j + 1;
}

Replacement of English numbers of a string with Arabic numbers

I'm going to find number chars in a String and replace them with their Arabic versions.
The Code is:
public static void main(String[] args) {
String pattern = "[0-9]+";
Pattern p = Pattern.compile(pattern);
String mainText = "34titi685dytti5685fjjfj8585443";
Matcher m = p.matcher(mainText);
int i = 0;
while (m.find()) {
System.out.println("Match number " + i);
String tmp = m.group();
char[] cTmp = tmp.toCharArray();
for (int j = 0; j < cTmp.length; j++) {
cTmp[j] = (char) ((int) cTmp[j] + 1584);
}
m.group().replaceFirst(tmp,new String(cTmp));
i++;
}
System.out.println(mainText);
}
But at the end it prints the same string mainText.
What is wrong with my code?
This is not how you do a replacement using Matcher. m.group() just gives you the matched part of the string. Whatever replacement you do in it, you have to perform concatenation with original string. This is due to the fact that Strings are immutable objects. You don't perform in-place replacement to it.
The proper way to do this is to create a StringBuffer object, and use Matcher#appendReplacement and Matcher#appendTail methods.
You do it like this:
StringBuffer buffer = new StringBuffer();
while (m.find()) {
String tmp = m.group();
char[] cTmp = tmp.toCharArray();
for (int j = 0; j < cTmp.length; j++) {
cTmp[j] = (char) (cTmp[j] + 1584); // You don't need to typecast `cTmp[j]` to `int`.
}
m.appendReplacement(buffer, new String(cTmp));
}
m.appendTail(buffer);
System.out.println(buffer.toString());
String is final and immutable, you have to assign the new string to itself.
use StringBuilder to append the values every time.
StringBuilder stringBuilder = new StringBuilder();
//
//
m.group().replaceFirst(stringBuilder,new String(cTmp));
instead of
m.group().replaceFirst(tmp,new String(cTmp));
Assign the outcoming value to the mainText

How to replace all characters in a Java string with stars

I want to replace all the characters in a Java String with * character. So it shouldn't matter what character it is, it should be replaced with a *.
I know there are heaps of examples there on internet but have not one that replaces every character and I have tried myself but no success.
Java 11 and later
str = "*".repeat(str.length());
Note: This replaces newlines \n with *. If you want to preserve \n, see solution below.
Java 10 and earlier
str = str.replaceAll(".", "*");
This preserves newlines.
To replace newlines with * as well in Java 10 and earlier, you can use:
str = str.replaceAll("(?s).", "*");
The (?s) doesn't match anything but activates DOTALL mode which makes . also match \n.
Don't use regex at all, count the String length, and return the according number of stars.
Plain Java < 8 Version:
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for(int i = =; i < len; i++){
sb.append('*');
}
return sb.toString();
Plain Java >= 8 Version:
int len = str.length();
return IntStream.range(0, n).mapToObj(i -> "*").collect(Collectors.joining());
Using Guava:
return Strings.repeat("*", str.length());
// OR
return CharMatcher.ANY.replaceFrom(str, '*');
Using Commons / Lang:
return StringUtils.repeat("*", str.length());
System.out.println("foobar".replaceAll(".", "*"));
public String allStar(String s) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
sb.append('*');
}
return sb.toString();
}
How abt creating a new string with the number of * = number of last string char?
StringBuffer bf = new StringBuffer();
for (int i = 0; i < source.length(); i++ ) {
bf.append('*');
}
There may be other faster/better ways to do it, but you could just use a string buffer and a for-loop:
public String stringToAsterisk(String input) {
if (input == null) return "";
StringBuffer sb = new StringBuffer();
for (int x = 0; x < input.length(); x++) {
sb.append("*");
}
return sb.toString();
}
If your application is single threaded, you can use StringBuilder instead, but it's not thread safe.
I am not sure if this might be any faster:
public String stringToAsterisk(String input) {
if (input == null) return "";
int length = input.length();
char[] chars = new char[length];
while (length > 0) chars[--length] = "*";
return new String(chars);
}
Without any external library and without your own loop, you can do:
String input = "Hello";
char[] ca = new char[input.length()];
Arrays.fill(ca, '*');
String output = new String(ca);
BTW, both Arrays.fill() and String(char []) are really fast.
Recursive method
String nCopies(String s, int n) {
return n == 1 ? s.replaceFirst(".$", "") : nCopies(s + s, --n);
}
String text = "Hello World";
System.out.println( text.replaceAll( "[A-Za-z0-9]", "*" ) );
output : ***** *****

Categories

Resources