break large String into small Strings - java

i have a large string which contains Id's example :
HD47-4585-GG89
here at the above i have an id of a single object but sometimes it may contain id's of multiple objects like this :
HD47-4585-GG89-KO89-9089-RT45
the above haves ids of 2 objects now i want to convert the above string to an array or in multiple small Strings
something like :
id1 = HD47-4585-GG89
id2 = KO89-9089-RT45
every single id haves a fixed number of characters in it here its 14 (counting the symbols too) and the number of total id's in a single String is not determined
i dont know how to do it any one can guide me with this ?
i think all i have to do is clip the first 14 characters of string then assign a variable to it and repeat this until string is empty

You could also use regex:
String input = "HD47-4585-GG89-KO89-9089-RT45";
Pattern id = Pattern.compile("(\\w{4}-\\w{4}-\\w{4})");
Matcher matcher = id.matcher(input);
List<String> ids = new ArrayList<>();
while(matcher.find()) {
ids.add(matcher.group(1));
}
System.out.println(ids); // [HD47-4585-GG89, KO89-9089-RT45]
See Ideone.
Although this assumes that each group of characters (HD47) is 4 long.

Using guava Splitter
class SplitIt
{
public static void main (String[] args) throws java.lang.Exception
{
String idString = "HD47-4585-GG89-KO89-9089-RT45-HD47-4585-GG89";
Iterable<String> result = Splitter
.fixedLength(15)
.trimResults(CharMatcher.inRange('-', '-'))
.split(idString);
String[] parts = Iterables.toArray(result, String.class);
for (String id : parts) {
System.out.println(id);
}
}
}

StringTokenizer st = new StringTokenizer(String,"-");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
these tokens can be stored in some arrays and then using index you can get required data.

String text = "HD47-4585-GG89-KO89-9089-RT45";
String first = "";
String second = "";
List<String> textList = Arrays.asList(text.split("-"));
for (int i = 0; i < textList.size() / 2; i++) {
first += textList.get(i) + "-";
}
for (int i = textList.size() / 2; i < textList.size(); i++) {
second += textList.get(i) + "-";
}
first = first.substring(0, first.length() - 1);
second = second.substring(0, second.length() - 1);

Related

How to sort a array that contains special characters alphabetically?

I am looking for code that produces the following output in standard output from the following string prepared according to a certain format.
Assumptions and rules:
Each letter is used 2 times in the given string and the letters between the same 2 letters are to be considered child letters.
The given string is always given in proper format. The string format
does not need to be checked.
Example:
Input : abccbdeeda
Expected output:
a
--b
----c
--d
----e
Explanation: since the 2 letters "b" occur between the letters "a", the letter b takes 2 hyphens (--b)
Attempt
public static void main(String[] args) {
String input = "abccbdeeda";
System.out.println("input: " + input);
String[] strSplit = input.split("");
String g = "";
String h = "-";
ArrayList<String> list = new ArrayList<String>();
int counter = 1;
boolean secondNumber;
list.add(strSplit[0]);
int dual = 0;
for (int i = 1; i < strSplit.length; i++) {
secondNumber = list.contains(strSplit[i]);
if ((secondNumber)) {
counter--;
dual = counter * 2;
for (int f = 0; f < dual; f++) {
strSplit[i] = h.concat(strSplit[i]);
}
g = "";
dual = 0;
} else {
list.add(strSplit[i]);
counter++;
}
}
Arrays.sort(strSplit);
for (int p = 0; p < strSplit.length; p++) {
System.out.println(strSplit[p]);
}
}
input: abccbdeeda
My output:
----c
----e
--b
--d
a
I wasn't able to sort the output alphabetically. How can I sort alphabetically with those hyphen characters in them?
This task is nicely done with the help of a stack. If the current character is equal to the top of the stack, then the character is closed and can be removed, otherwise we met it for the first time and it must be added to the stack and the resulting string by adding before it stack.size() * 2 dashes.
When we have completely traversed the string we can sort the resulting string.
public static void main(String[] args) {
Stack<Character> stack = new Stack<>();
String string = "abccbdeeda";
StringBuilder result = new StringBuilder();
for(int i = 0; i < string.length(); i++) {
char curChar = string.charAt(i);
if(!stack.isEmpty() && curChar == stack.peek()) {
stack.pop();
} else {
result.append("-".repeat(stack.size() * 2)).append(curChar).append(" ");
stack.add(curChar);
}
}
System.out.println(result);
System.out.println(Arrays.toString(Arrays.stream(result.toString().split(" ")).sorted().toArray()));
}
Output
a --b ----c --d ----e
[----c, ----e, --b, --d, a]
You can go through the strSplit array and extract the charactors in each element to a separate list/array. To check whether the array element contains a letter you can write a regular expression.
Ex: private final Pattern x = Pattern.compile("[a-z]");
Write a separate method to match the patern to each element in the strSplit array. This method will return the charactor in your input string.
private String findCharactor(final StringBuilder element) {
final Matcher matcher = x.matcher(element);
if (matcher.find()) {
final int matchIndex = matcher.start(); //this gives the index of the char in the string
return element.substring(matchIndex);
}
}
Add these returned charactors to a separate array and sort it using sorting function.
Suppose your result list is:
List<String> resultList = Arrays.asList("----c", "----e", "--b", "--d", "a");
You can sort it alphabetically by a single line:
Collections.sort(resultList, (o1, o2) -> new StringBuilder(o1).reverse().toString().compareTo(new StringBuilder(o2).reverse().toString()));
You can use recursion for a depth-first traversal (preorder):
public static String dfs(String string, String prefix) {
if (string.length() == 0) return "";
int i = string.indexOf(string.charAt(0), 1);
return prefix + string.charAt(0) + "\n" // current
+ dfs(string.substring(1, i), prefix + "--") // all nested
+ dfs(string.substring(i + 1), prefix); // all siblings
}
Example call:
public static void main(String[] args) {
System.out.println(dfs("abccbdeeda", ""));
}

How to split a String by a comma, but from the second comma

I have a string as:
"model=iPhone12,3,os_version=13.6.1,os_update_exist=1,status=1"
How can I convert this into:
model=iPhone12,3
os_version=13.6.1
os_update_exist=1
status=1
Split the string from the first comma, then re-join the first two elements of the resulting string array.
I doubt there's a "clean" way to do this but this would work for your case:
String str = "model=iPhone12,3,os_version=13.6.1,os_update_exist=1,status=1";
String[] sp = str.split(",");
sp[0] += "," + sp[1];
sp[1] = sp[2];
sp[2] = sp[3];
sp[3] = sp[4];
sp[4] = "";
You can try this:
public String[] splitString(String source) {
// Split the source string based on a comma followed by letters and numbers.
// Basically "model=iPhone12,3,os_version=13.6.1,os_update_exist=1,status=1" will be split
// like this:
// model=iPhone12,3
// ,os_version=13.6.1
// ,os_update_exist=1
// ,status=1"
String[] result = source.split("(?=,[a-z]+\\d*)");
for (int i = 0; i < result.length; i++) {
// Removes the comma at the beginning of the string if present
if (result[i].matches(",.*")) {
result[i] = result[i].substring(1);
}
}
return result;
}
if you are parsing always the same kind of String a regex like this will be do the job
String str = "model=iPhone12,3,os_version=13.6.1,os_update_exist=1,status=1";
Matcher m = Pattern.compile("model=(.*),os_version=(.*),os_update_exist=(.*),status=(.*)").matcher(str);
if (m.find()) {
model = m.group(1)); // iPhone12,3
os = m.group(2)); // 13.6.1
update = m.group(3)); // 1
status = m.group(4)); // 1
}
If you really wants to use a split you can still use that kind of trick
String[] split = str.replaceAll(".*?=(.*?)(,[a-z]|$)", "$1#")
.split("#");
split[0] // iPhone12,3
split[1] // 13.6.1
split[2] // 1
split[3] // 1

Find space separated names using Apache OpenNLP

I am using NER of Apache Open NLP. I have successfully trained my custom data. And while using the name finder, I am splitting the given string based on white space and passing the string array as given below.
NameFinderME nameFinder = new NameFinderME(model);
String []sentence = input.split(" "); //eg:- input = Give me list of test case in project X
Span nameSpans[] = nameFinder.find(sentence);
Here, when I use split, test and case are given as separate values and is never detected by the namefinder. How would I possibly overcome the above issue. Is there a way by which I can pass the complete string (without splitting it into array) such that, test case will be considered as a whole by itself ?
You can do it using regular expressions. Try replacing the second line with this:
String []sentence = input.split("\\s(?<!(\\stest\\s(?=case\\s)))");
Maybe there is a better way to write the expression, but this works for me and the output is:
Give
me
list
of
test case
in
project
X
EDIT: If you are interested in the details check here where I split: https://regex101.com/r/6HLBnL/1
EDIT 2: If you have many words that don't get separated, I wrote a method that generates the regex for you. This is how the regex in this case should look like (if you don't want to separate 'test case' and 'in project'):
\s(?<!(\stest\s(?=case\s))|(\sin\s(?=project\s)))
Following is a simple program to demonstrate it. In this example you just put the words that don't need separation in the array unseparated.
class NoSeparation {
private static String[][] unseparated = {{"test", "case"}, {"in", "project"}};
private static String getRegex() {
String regex = "\\s(?<!";
for (int i = 0; i < unseparated.length; i++)
regex += "(\\s" + separated[i][0] + "\\s(?=" + separated[i][1] + "\\s))|";
// Remove the last |
regex = regex.substring(0, regex.length() - 1);
return (regex + ")");
}
public static void main(String[] args) {
String input = "Give me list of test case in project X";
String []sentence = input.split(getRegex());
for (String i: sentence)
System.out.println(i);
}
}
EDIT 3: Following is a very dirty way to handle strings with more than 2 words. It works, but I am pretty sure that you can do it in a more efficient way. It will work fine in short inputs, but in longer it will probably be slow.
You have to put the words that should not be splitted in a 2d array, as in unseparated. You should also choose a separator if you don't want to use %% for some reason (e.g. if there is a chance your input contains it).
class NoSeparation {
private static final String SEPARATOR = "%%";
private static String[][] unseparated = {{"of", "test", "case"}, {"in", "project"}};
private static String[] splitString(String in) {
String[] splitted;
for (int i = 0; i < unseparated.length; i++) {
String toReplace = "";
String replaceWith = "";
for (int j = 0; j < unseparated[i].length; j++) {
toReplace += unseparated[i][j] + ((j < unseparated[i].length - 1)? " " : "");
replaceWith += unseparated[i][j] + ((j < unseparated[i].length - 1)? SEPARATOR : "");
}
in = in.replaceAll(toReplace, replaceWith);
}
splitted = in.split(" ");
for (int i = 0; i < splitted.length; i++)
splitted[i] = splitted[i].replaceAll(SEPARATOR, " ");
return splitted;
}
public static void main(String[] args) {
String input = "Give me list of test case in project X";
// Uncomment this if there is a chance to have multiple spaces/tabs
// input = input.replaceAll("[\\s\\t]+", " ");
for (String str: splitString(input))
System.out.println(str);
}
}

How can I replace two strings in a way that one does not end up replacing the other?

Let's say that I have the following code:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);
After this code runs, the value of story will be "Once upon a time, there was a foo and a foo."
A similar issue occurs if I replaced them in the opposite order:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);
The value of story will be "Once upon a time, there was a bar and a bar."
My goal is to turn story into "Once upon a time, there was a bar and a foo." How could I accomplish that?
Use the replaceEach() method from Apache Commons StringUtils:
StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})
You use an intermediate value (which is not yet present in the sentence).
story = story.replace("foo", "lala");
story = story.replace("bar", "foo");
story = story.replace("lala", "bar");
As a response to criticism: if you use a large enough uncommon string like zq515sqdqs5d5sq1dqs4d1q5dqqé"&é5d4sqjshsjddjhodfqsqc, nvùq^µù;d&€sdq: d: ;)àçàçlala and use that, it is unlikely to the point where I won't even debate it that a user will ever enter this. The only way to know whether a user will is by knowing the source code and at that point you're with a whole other level of worries.
Yes, maybe there are fancy regex ways. I prefer something readable that I know will not break out on me either.
Also reiterating the excellent advise given by #David Conrad in the comments:
Don't use some string cleverly (stupidly) chosen to be unlikely. Use characters from the Unicode Private Use Area, U+E000..U+F8FF. Remove any such characters first, since they shouldn't legitimately be in the input (they only have application-specific meaning within some application), then use them as placeholders when replacing.
You can try something like this, using Matcher#appendReplacement and Matcher#appendTail:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
Pattern p = Pattern.compile("foo|bar");
Matcher m = p.matcher(story);
StringBuffer sb = new StringBuffer();
while (m.find()) {
/* do the swap... */
switch (m.group()) {
case "foo":
m.appendReplacement(sb, word1);
break;
case "bar":
m.appendReplacement(sb, word2);
break;
default:
/* error */
break;
}
}
m.appendTail(sb);
System.out.println(sb.toString());
Once upon a time, there was a bar and a foo.
This is not an easy problem. And the more search-replacement parameters you have, the trickier it gets. You have several options, scattered on the palette of ugly-elegant, efficient-wasteful:
Use StringUtils.replaceEach from Apache Commons as #AlanHay recommended. This is a good option if you're free to add new dependencies in your project. You might get lucky: the dependency might be included already in your project
Use a temporary placeholder as #Jeroen suggested, and perform the replacement in 2 steps:
Replace all search patterns with a unique tag that doesn't exist in the original text
Replace the placeholders with the real target replacement
This is not a great approach, for several reasons: it needs to ensure that the tags used in the first step are really unique; it performs more string replacement operations than really necessary
Build a regex from all the patterns and use the method with Matcher and StringBuffer as suggested by #arshajii. This is not terrible, but not that great either, as building the regex is kind of hackish, and it involves StringBuffer which went out of fashion a while ago in favor of StringBuilder.
Use a recursive solution proposed by #mjolka, by splitting the string at the matched patterns, and recursing on the remaining segments. This is a fine solution, compact and quite elegant. Its weakness is the potentially many substring and concatenation operations, and the stack size limits that apply to all recursive solutions
Split the text to words and use Java 8 streams to perform the replacements elegantly as #msandiford suggested, but of course that only works if you are ok with splitting at word boundaries, which makes it not suitable as a general solution
Here's my version, based on ideas borrowed from Apache's implementation. It's neither simple nor elegant, but it works, and should be relatively efficient, without unnecessary steps. In a nutshell, it works like this: repeatedly find the next matching search pattern in the text, and use a StringBuilder to accumulate the unmatched segments and the replacements.
public static String replaceEach(String text, String[] searchList, String[] replacementList) {
// TODO: throw new IllegalArgumentException() if any param doesn't make sense
//validateParams(text, searchList, replacementList);
SearchTracker tracker = new SearchTracker(text, searchList, replacementList);
if (!tracker.hasNextMatch(0)) {
return text;
}
StringBuilder buf = new StringBuilder(text.length() * 2);
int start = 0;
do {
SearchTracker.MatchInfo matchInfo = tracker.matchInfo;
int textIndex = matchInfo.textIndex;
String pattern = matchInfo.pattern;
String replacement = matchInfo.replacement;
buf.append(text.substring(start, textIndex));
buf.append(replacement);
start = textIndex + pattern.length();
} while (tracker.hasNextMatch(start));
return buf.append(text.substring(start)).toString();
}
private static class SearchTracker {
private final String text;
private final Map<String, String> patternToReplacement = new HashMap<>();
private final Set<String> pendingPatterns = new HashSet<>();
private MatchInfo matchInfo = null;
private static class MatchInfo {
private final String pattern;
private final String replacement;
private final int textIndex;
private MatchInfo(String pattern, String replacement, int textIndex) {
this.pattern = pattern;
this.replacement = replacement;
this.textIndex = textIndex;
}
}
private SearchTracker(String text, String[] searchList, String[] replacementList) {
this.text = text;
for (int i = 0; i < searchList.length; ++i) {
String pattern = searchList[i];
patternToReplacement.put(pattern, replacementList[i]);
pendingPatterns.add(pattern);
}
}
boolean hasNextMatch(int start) {
int textIndex = -1;
String nextPattern = null;
for (String pattern : new ArrayList<>(pendingPatterns)) {
int matchIndex = text.indexOf(pattern, start);
if (matchIndex == -1) {
pendingPatterns.remove(pattern);
} else {
if (textIndex == -1 || matchIndex < textIndex) {
textIndex = matchIndex;
nextPattern = pattern;
}
}
}
if (nextPattern != null) {
matchInfo = new MatchInfo(nextPattern, patternToReplacement.get(nextPattern), textIndex);
return true;
}
return false;
}
}
Unit tests:
#Test
public void testSingleExact() {
assertEquals("bar", StringUtils.replaceEach("foo", new String[]{"foo"}, new String[]{"bar"}));
}
#Test
public void testReplaceTwice() {
assertEquals("barbar", StringUtils.replaceEach("foofoo", new String[]{"foo"}, new String[]{"bar"}));
}
#Test
public void testReplaceTwoPatterns() {
assertEquals("barbaz", StringUtils.replaceEach("foobar",
new String[]{"foo", "bar"},
new String[]{"bar", "baz"}));
}
#Test
public void testReplaceNone() {
assertEquals("foofoo", StringUtils.replaceEach("foofoo", new String[]{"x"}, new String[]{"bar"}));
}
#Test
public void testStory() {
assertEquals("Once upon a foo, there was a bar and a baz, and another bar and a cat.",
StringUtils.replaceEach("Once upon a baz, there was a foo and a bar, and another foo and a cat.",
new String[]{"foo", "bar", "baz"},
new String[]{"bar", "baz", "foo"})
);
}
Search for the first word to be replaced. If it's in the string, recurse on the the part of the string before the occurrence, and on the part of the string after the occurrence.
Otherwise, continue with the next word to be replaced.
A naive implementation might look like this
public static String replaceAll(String input, String[] search, String[] replace) {
return replaceAll(input, search, replace, 0);
}
private static String replaceAll(String input, String[] search, String[] replace, int i) {
if (i == search.length) {
return input;
}
int j = input.indexOf(search[i]);
if (j == -1) {
return replaceAll(input, search, replace, i + 1);
}
return replaceAll(input.substring(0, j), search, replace, i + 1) +
replace[i] +
replaceAll(input.substring(j + search[i].length()), search, replace, i);
}
Sample usage:
String input = "Once upon a baz, there was a foo and a bar.";
String[] search = new String[] { "foo", "bar", "baz" };
String[] replace = new String[] { "bar", "baz", "foo" };
System.out.println(replaceAll(input, search, replace));
Output:
Once upon a foo, there was a bar and a baz.
A less-naive version:
public static String replaceAll(String input, String[] search, String[] replace) {
StringBuilder sb = new StringBuilder();
replaceAll(sb, input, 0, input.length(), search, replace, 0);
return sb.toString();
}
private static void replaceAll(StringBuilder sb, String input, int start, int end, String[] search, String[] replace, int i) {
while (i < search.length && start < end) {
int j = indexOf(input, search[i], start, end);
if (j == -1) {
i++;
} else {
replaceAll(sb, input, start, j, search, replace, i + 1);
sb.append(replace[i]);
start = j + search[i].length();
}
}
sb.append(input, start, end);
}
Unfortunately, Java's String has no indexOf(String str, int fromIndex, int toIndex) method. I've omitted the implementation of indexOf here as I'm not certain it's correct, but it can be found on ideone, along with some rough timings of various solutions posted here.
One-liner in Java 8:
story = Pattern
.compile(String.format("(?<=%1$s)|(?=%1$s)", "foo|bar"))
.splitAsStream(story)
.map(w -> ImmutableMap.of("bar", "foo", "foo", "bar").getOrDefault(w, w))
.collect(Collectors.joining());
Lookaround regular expressions (?<=, ?=): http://www.regular-expressions.info/lookaround.html
If the words can contain special regex characters, use Pattern.quote to
escape them.
I use guava ImmutableMap for conciseness, but obviously any other Map will do the job as well.
Here is a Java 8 streams possibility that might be interesting for some:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);
// Split on word boundaries so we retain whitespace.
String translated = Arrays.stream(story.split("\\b"))
.map(w -> wordMap.getOrDefault(w, w))
.collect(Collectors.joining());
System.out.println(translated);
Here is an approximation of the same algorithm in Java 7:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);
// Split on word boundaries so we retain whitespace.
StringBuilder translated = new StringBuilder();
for (String w : story.split("\\b"))
{
String tw = wordMap.get(w);
translated.append(tw != null ? tw : w);
}
System.out.println(translated);
​If you want to replace words in a sentence which are separated by white space as shown in your example you can use this simple algorithm.
Split story on white space
Replace each elements, if foo replace it to bar and vice varsa
Join the array back into one string
​If Splitting on space is not acceptable one can follow this alternate algorithm. ​You need to use the longer string first. If the stringes are foo and fool, you need to use fool first and then foo.
Split on the word foo
Replace bar with foo each element of the array
Join that array back adding bar after each element except the last
Here's a less complicated answer using Map.
private static String replaceEach(String str,Map<String, String> map) {
Object[] keys = map.keySet().toArray();
for(int x = 0 ; x < keys.length ; x ++ ) {
str = str.replace((String) keys[x],"%"+x);
}
for(int x = 0 ; x < keys.length ; x ++) {
str = str.replace("%"+x,map.get(keys[x]));
}
return str;
}
And method is called
Map<String, String> replaceStr = new HashMap<>();
replaceStr.put("Raffy","awesome");
replaceStr.put("awesome","Raffy");
String replaced = replaceEach("Raffy is awesome, awesome awesome is Raffy Raffy", replaceStr);
Output is:
awesome is Raffy, Raffy Raffy is awesome awesome
If you want to be able to handle multiple occurrences of the search strings to be replaced, you can do that easily by splitting the string on each search term, then replacing it.
Here is an example:
String regex = word1 + "|" + word2;
String[] values = Pattern.compile(regex).split(story);
String result;
foreach subStr in values
{
subStr = subStr.replace(word1, word2);
subStr = subStr.replace(word2, word1);
result += subStr;
}
You can accomplish your goal with the following code block:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = String.format(story.replace(word1, "%1$s").replace(word2, "%2$s"),
word2, word1);
It replaces the words regardless of the order. You can extend this principle into an utility method, like:
private static String replace(String source, String[] targets, String[] replacements) throws IllegalArgumentException {
if (source == null) {
throw new IllegalArgumentException("The parameter \"source\" cannot be null.");
}
if (targets == null || replacements == null) {
throw new IllegalArgumentException("Neither parameters \"targets\" or \"replacements\" can be null.");
}
if (targets.length == 0 || targets.length != replacements.length) {
throw new IllegalArgumentException("The parameters \"targets\" and \"replacements\" must have at least one item and have the same length.");
}
String outputMask = source;
for (int i = 0; i < targets.length; i++) {
outputMask = outputMask.replace(targets[i], "%" + (i + 1) + "$s");
}
return String.format(outputMask, (Object[])replacements);
}
Which would be consumed as:
String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = replace(story, new String[] { "bar", "foo" },
new String[] { "foo", "bar" }));
This works and is simple:
public String replaceBoth(String text, String token1, String token2) {
return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
}
You use it like this:
replaceBoth("Once upon a time, there was a foo and a bar.", "foo", "bar");
Note: this counts on Strings not containing character \ufdd0, which is a character permanently reserved for internal use by Unicode (See http://www.unicode.org/faq/private_use.html):
I don't think it's necessary, but If you want to be absolutely safe you can use:
public String replaceBoth(String text, String token1, String token2) {
if (text.contains("\ufdd0") || token1.contains("\ufdd0") || token2.contains("\ufdd0")) throw new IllegalArgumentException("Invalid character.");
return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
}
Swapping Only One Occurrence
If there is only one occurrence of each of the swapable strings in the input, you can do the following:
Before proceeding to any replace, get the indices of the occurrences of the words. After that we only replace the word found at these indexes, and not all occurrences. This solution uses StringBuilder and does not produce intermediate Strings like String.replace().
One thing to note: if the swapable words have different lengths, after the first replace the second index might change (if the 1st word occurs before the 2nd) exactly with the difference of the 2 lengths. So aligning the second index will ensure this works even if we're swapping words with different lengths.
public static String swap(String src, String s1, String s2) {
StringBuilder sb = new StringBuilder(src);
int i1 = src.indexOf(s1);
int i2 = src.indexOf(s2);
sb.replace(i1, i1 + s1.length(), s2); // Replace s1 with s2
// If s1 was before s2, idx2 might have changed after the replace
if (i1 < i2)
i2 += s2.length() - s1.length();
sb.replace(i2, i2 + s2.length(), s1); // Replace s2 with s1
return sb.toString();
}
Swapping Arbitrary Number of Occurrences
Analogous to the previous case we will first collect the indexes (occurrences) of the words, but in this case it will a list of integers for each word, not just one int. For this we will use the following utility method:
public static List<Integer> occurrences(String src, String s) {
List<Integer> list = new ArrayList<>();
for (int idx = 0;;)
if ((idx = src.indexOf(s, idx)) >= 0) {
list.add(idx);
idx += s.length();
} else
return list;
}
And using this we will replace the words with the other one by decreasing index (which might require to alternate between the 2 swapable words) so that we won't even have to correct the indices after a replace:
public static String swapAll(String src, String s1, String s2) {
List<Integer> l1 = occurrences(src, s1), l2 = occurrences(src, s2);
StringBuilder sb = new StringBuilder(src);
// Replace occurrences by decreasing index, alternating between s1 and s2
for (int i1 = l1.size() - 1, i2 = l2.size() - 1; i1 >= 0 || i2 >= 0;) {
int idx1 = i1 < 0 ? -1 : l1.get(i1);
int idx2 = i2 < 0 ? -1 : l2.get(i2);
if (idx1 > idx2) { // Replace s1 with s2
sb.replace(idx1, idx1 + s1.length(), s2);
i1--;
} else { // Replace s2 with s1
sb.replace(idx2, idx2 + s2.length(), s1);
i2--;
}
}
return sb.toString();
}
It's easy to write a method to do this using String.regionMatches:
public static String simultaneousReplace(String subject, String... pairs) {
if (pairs.length % 2 != 0) throw new IllegalArgumentException(
"Strings to find and replace are not paired.");
StringBuilder sb = new StringBuilder();
outer:
for (int i = 0; i < subject.length(); i++) {
for (int j = 0; j < pairs.length; j += 2) {
String find = pairs[j];
if (subject.regionMatches(i, find, 0, find.length())) {
sb.append(pairs[j + 1]);
i += find.length() - 1;
continue outer;
}
}
sb.append(subject.charAt(i));
}
return sb.toString();
}
Testing:
String s = "There are three cats and two dogs.";
s = simultaneousReplace(s,
"cats", "dogs",
"dogs", "budgies");
System.out.println(s);
Output:
There are three dogs and two budgies.
It is not immediately obvious, but a function like this can still be dependent on the order in which the replacements are specified. Consider:
String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplace(truth,
"JavaScript", "Hamster",
"Java", "Ham");
System.out.println(truth);
Output:
Java is to JavaScript as Ham is to Hamster
But reverse the replacements:
truth += " as " + simultaneousReplace(truth,
"Java", "Ham",
"JavaScript", "Hamster");
Output:
Java is to JavaScript as Ham is to HamScript
Oops! :)
Therefore it is sometimes useful to make sure to look for the longest match (as PHP's strtr function does, for example). This version of the method will do that:
public static String simultaneousReplace(String subject, String... pairs) {
if (pairs.length % 2 != 0) throw new IllegalArgumentException(
"Strings to find and replace are not paired.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < subject.length(); i++) {
int longestMatchIndex = -1;
int longestMatchLength = -1;
for (int j = 0; j < pairs.length; j += 2) {
String find = pairs[j];
if (subject.regionMatches(i, find, 0, find.length())) {
if (find.length() > longestMatchLength) {
longestMatchIndex = j;
longestMatchLength = find.length();
}
}
}
if (longestMatchIndex >= 0) {
sb.append(pairs[longestMatchIndex + 1]);
i += longestMatchLength - 1;
} else {
sb.append(subject.charAt(i));
}
}
return sb.toString();
}
Note that the above methods are case-sensitive. If you need a case-insensitive version it is easy to modify the above because String.regionMatches can take an ignoreCase parameter.
If you don't want any dependencies, you could simply use an array which allows a one-time change only. This is not the most efficient solution, but it should work.
public String replace(String sentence, String[]... replace){
String[] words = sentence.split("\\s+");
int[] lock = new int[words.length];
StringBuilder out = new StringBuilder();
for (int i = 0; i < words.length; i++) {
for(String[] r : replace){
if(words[i].contains(r[0]) && lock[i] == 0){
words[i] = words[i].replace(r[0], r[1]);
lock[i] = 1;
}
}
out.append((i < (words.length - 1) ? words[i] + " " : words[i]));
}
return out.toString();
}
Then, it whould work.
String story = "Once upon a time, there was a foo and a bar.";
String[] a = {"foo", "bar"};
String[] b = {"bar", "foo"};
String[] c = {"there", "Pocahontas"};
story = replace(story, a, b, c);
System.out.println(story); // Once upon a time, Pocahontas was a bar and a foo.
You are performing multiple search-replace operations on the input. This will produce undesired results when the replacement strings contain search strings. Consider the foo->bar, bar-foo example, here are the results for each iteration:
Once upon a time, there was a foo and a bar. (input)
Once upon a time, there was a bar and a bar. (foo->bar)
Once upon a time, there was a foo and a foo. (bar->foo, output)
You need to perform the replacement in one iteration without going back. A brute-force solution is as follows:
Search the input from current position to end for multiple search strings until a match is found
Replace the matched search string with corresponding replace string
Set current position to the next character after the replaced string
Repeat
A function such as String.indexOfAny(String[]) -> int[]{index, whichString} would be useful. Here is an example (not the most efficient one):
private static String replaceEach(String str, String[] searchWords, String[] replaceWords) {
String ret = "";
while (str.length() > 0) {
int i;
for (i = 0; i < searchWords.length; i++) {
String search = searchWords[i];
String replace = replaceWords[i];
if (str.startsWith(search)) {
ret += replace;
str = str.substring(search.length());
break;
}
}
if (i == searchWords.length) {
ret += str.substring(0, 1);
str = str.substring(1);
}
}
return ret;
}
Some tests:
System.out.println(replaceEach(
"Once upon a time, there was a foo and a bar.",
new String[]{"foo", "bar"},
new String[]{"bar", "foo"}
));
// Once upon a time, there was a bar and a foo.
System.out.println(replaceEach(
"a p",
new String[]{"a", "p"},
new String[]{"apple", "pear"}
));
// apple pear
System.out.println(replaceEach(
"ABCDE",
new String[]{"A", "B", "C", "D", "E"},
new String[]{"B", "C", "E", "E", "F"}
));
// BCEEF
System.out.println(replaceEach(
"ABCDEF",
new String[]{"ABCDEF", "ABC", "DEF"},
new String[]{"XXXXXX", "YYY", "ZZZ"}
));
// XXXXXX
// note the order of search strings, longer strings should be placed first
// in order to make the replacement greedy
Demo on IDEONE
Demo on IDEONE, alternate code
You can always replace it with a word you are sure will appear nowhere else in the string, and then do the second replace later:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", "StringYouAreSureWillNeverOccur").replace("bar", "word2").replace("StringYouAreSureWillNeverOccur", "word1");
Note that this will not work right if "StringYouAreSureWillNeverOccur" does occur.
Consider using StringBuilder
Then store the index where each string should start. If you use a place holder character at each position, then remove it, and insert the users string. You can then map the end position by adding the string length to the start position.
String firstString = "???";
String secondString = "???"
StringBuilder story = new StringBuilder("One upon a time, there was a "
+ firstString
+ " and a "
+ secondString);
int firstWord = 30;
int secondWord = firstWord + firstString.length() + 7;
story.replace(firstWord, firstWord + firstString.length(), userStringOne);
story.replace(secondWord, secondWord + secondString.length(), userStringTwo);
firstString = userStringOne;
secondString = userStringTwo;
return story;
What I can only share is my own method.
You can use a temporary String temp = "<?>"; or String.Format();
This is my example code created in console application via c# -"Idea Only, Not Exact Answer".
static void Main(string[] args)
{
String[] word1 = {"foo", "Once"};
String[] word2 = {"bar", "time"};
String story = "Once upon a time, there was a foo and a bar.";
story = Switcher(story,word1,word2);
Console.WriteLine(story);
Console.Read();
}
// Using a temporary string.
static string Switcher(string text, string[] target, string[] value)
{
string temp = "<?>";
if (target.Length == value.Length)
{
for (int i = 0; i < target.Length; i++)
{
text = text.Replace(target[i], temp);
text = text.Replace(value[i], target[i]);
text = text.Replace(temp, value[i]);
}
}
return text;
}
Or you can also use the String.Format();
static string Switcher(string text, string[] target, string[] value)
{
if (target.Length == value.Length)
{
for (int i = 0; i < target.Length; i++)
{
text = text.Replace(target[i], "{0}").Replace(value[i], "{1}");
text = String.Format(text, value[i], target[i]);
}
}
return text;
}
Output: time upon a Once, there was a bar and a foo.
Here's my version, which is word-based:
class TextReplace
{
public static void replaceAll (String text, String [] lookup,
String [] replacement, String delimiter)
{
String [] words = text.split(delimiter);
for (int i = 0; i < words.length; i++)
{
int j = find(lookup, words[i]);
if (j >= 0) words[i] = replacement[j];
}
text = StringUtils.join(words, delimiter);
}
public static int find (String [] array, String key)
{
for (int i = 0; i < array.length; i++)
if (array[i].equals(key))
return i;
return (-1);
}
}
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
Little tricky way but you need to do some more checks.
1.convert string to character array
String temp[] = story.split(" ");//assume there is only spaces.
2.loop on temp and replace foo with bar and bar with foo as there are no chances of getting replaceable string again.
Well, the shorter answer is...
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
story = story.replace("foo", "#"+ word1).replace("bar", word2).replace("#" + word2, word1);
System.out.println(story);
Using the answer found here you can find all occurrences of the strings you wish to replace with.
So for example you run the code in the above SO answer. Create two tables of indexes (let's say bar and foo do not appear only once in your string) and you can work with those tables on replacing them in your string.
Now for replacing on specific index locations you can use:
public static String replaceStringAt(String s, int pos, String c) {
return s.substring(0,pos) + c + s.substring(pos+1);
}
Whereas pos is the index where your strings start (from the index tables I quoted above).
So let's say you created two tables of indexes for each one.
Let's call them indexBar and indexFoo.
Now in replacing them you could simply run two loops, one for each replacements you wish to make.
for(int i=0;i<indexBar.Count();i++)
replaceStringAt(originalString,indexBar[i],newString);
Similarly another loop for indexFoo.
This may not be as efficient as other answers here but it's simpler to understand than Maps or other stuff.
This would always give you the result you wish and for multiple possible occurrences of each string. As long as you store the index of each occurrence.
Also this answer needs no recursion nor any external dependencies. As far as complexity goes it propably is O(n squared), whereas n is the sum of occurences of both words.
I developed this code will solve problem:
public static String change(String s,String s1, String s2) {
int length = s.length();
int x1 = s1.length();
int x2 = s2.length();
int x12 = s.indexOf(s1);
int x22 = s.indexOf(s2);
String s3=s.substring(0, x12);
String s4 =s.substring(x12+3, x22);
s=s3+s2+s4+s1;
return s;
}
In the main use change(story,word2,word1).
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", "<foo />");
story = story.replace("bar", "<bar />");
story = story.replace("<foo />", word1);
story = story.replace("<bar />", word2);

How to split the string in java?

String str = "AlwinX-road-9:00pm-kanchana travels-25365445421";
String[] names = str.split("-");
I want output like following:
AlwinX-road
9:00pm
kanchana travels
25365445421
Use pattern matching to match your requirement
String str = "AlwinX-road-9:00pm-kanchana travels-25365445421";
String regex = "(^[A-Z-a-z ]+)[-]+(\\d+:\\d+pm)[-]([a-z]+\\s+[a-z]+)[-](\\d+)";
Matcher matcher = Pattern.compile( regex ).matcher( str);
while (matcher.find( ))
{
String roadname = matcher.group(1);
String time = matcher.group(2);
String travels = matcher.group(3);
String digits= matcher.group(4);
System.out.println("time="+time);
System.out.println("travels="+travels);
System.out.println("digits="+digits);
}
Since you want to include the delimiter in your first output line, you can do the split, and merge the first two element with a -: -
String[] names = str.split("-");
System.out.println(names[0] + "-" + names[1])
for (int i = 2;i < names.length; i++) {
System.out.println(names[i])
}
The split() method can't distinguish the dash in AlwinX-road and the other dashes in the string, it treats all the dashes the same. You will need to do some sort of post processing on the resulting array. If you will always need the first two strings in the array joined you can just do that. If your strings are more complex you will need to add additional logic to join the strings in the array.
One way you could do it, assuming the first '-' is always part of a two part identifier.
String str = "AlwinX-road-9:00pm-kanchana travels-25365445421";
String[] tokens = str.split("-");
String[] output = new String[tokens.length - 1];
output[0] = tokens[0] + '-' + tokens[1];
System.out.println(output[0]);
for(int i = 1; i < output.length; i++){
output[i] = tokens[i+1];
System.out.println(output[i]);
}
Looks like you want to split (with removal of all dashes but the first one).
String str = "AlwinX-road-9:00pm-kanchana travels-25365445421";
String[] names = str.split("-");
for (String value : names)
{
System.out.println(value);
}
So its produces:
AlwinX
road
9:00pm
kanchana travels
25365445421
Notice that "AlwinX" and "road" we split as well since they had a dash in between. So you will need custom logic to handle this case. here is an example how to do it (I used StringTokenizer):
StringTokenizer tk = new StringTokenizer(str, "-", true);
String firstString = null;
String secondString = null;
while (tk.hasMoreTokens())
{
final String token = tk.nextToken();
if (firstString == null)
{
firstString = token;
continue;
}
if (secondString == null && firstString != null && !token.equals("-"))
{
secondString = token;
System.out.println(firstString + "-" + secondString);
continue;
}
if (!token.equals("-"))
{
System.out.println(token);
}
}
This will produce:
AlwinX-road
9:00pm
kanchana travels
25365445421
from your format, I think you want to split the first one just before the time part. You can do it this way:
String str =yourString;
String beforetime=str.split("-\\d+:\\d+[ap]m")[0]; //this is your first token,
//AlwinX-road in your example
String rest=str.substring(beforetime.length()+1);
String[] restNames=rest.split("-");
If you really need it all together in one array then see the code below:
String[] allTogether=new String[restNames.length+1];//the string with all your tokens
allTogether[0]=beforetime;
System.arraycopy(restNames, 0, allTogether, 1, restNames.length);
If you use "_" as a separator instead of "-": AlwinX-road_9:00pm_kanchana travels_25365445421
New code:
String str = new String("AlwinX-road_9:00pm_kanchana travels_25365445421");
String separator = new String("_");
String[] names = str.split(separator);
for(int i=0; i<names.length; i++){
System.out.println(names[i]);
}

Categories

Resources