Reading and replacing Integers in a string - java

I have a string for example "x(10, 9, 8)" I want to read each integer from the string then using the integer as an array index retrieve a new integer from an array and replace it with this value.
All of the methods I've tried seem more suited to applying the same thing to all integers, or just retrieving the integers and then loosing track of them. Can anyone tell me the best way to do this please?

Using regular expressions, you can "browse" through each number in your string, regardless of how they are separated, and replace them as required. For example, the code below prints x(101, 99, 88):
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 88, 99, 101};
String s = "x(10, 9, 8)";
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(s);
StringBuilder replace = new StringBuilder();
int start = 0;
while(m.find()) {
//append the non-digit part first
replace.append(s.substring(start, m.start()));
start = m.end();
//parse the number and append the number in the array at that index
int index = Integer.parseInt(m.group());
replace.append(array[index]);
}
//append the end of the string
replace.append(s.substring(start, s.length()));
System.out.println(replace);
}
Note: you should add some exception handling.

Parse the numbers of your string using Integer.parseInt(), String.split(","), and String.indexOf() (for the ( and ). Create a List with them.
Iterate through this list and create a new List with the values from the array.
Iterate through the new List and create the response String.

Related

Simple delimiter parser

I'm trying out PetitParser for parsing a simple integer list delimited by commas. For example: "1, 2, 3, 4"
I tried creating a integer parser and then use the delimitedBy method.
Parser integerParser = digit().plus().flatten().trim().map((String value) -> Integer.parseInt(value));
Parser listParser = integerParser.delimitedBy(of(','));
List<Integer> list = listParser.parse(input).get();
This returns a list with the parsed integers but also the delimiters.
For example: [1, ,, 2, ,, 3, ,, 4]
Is there a way to exclude the delimiters from the result?
Yes, there is:
Parser listParser = integerParser
.delimitedBy(of(','))
.map(withoutSeparators());
To get withoutSeparators() import import static org.petitparser.utils.Functions.withoutSeparators;.
Here is an example of how to do it without any external library:
public static void main(String args[]) {
// source String
String delimitedNumbers = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10";
// split this String by its delimiter, a comma here
String[] delimitedNumbersSplit = delimitedNumbers.split(",");
// provide a data structure that holds numbers (integers) only
List<Integer> numberList = new ArrayList<>();
// for each part of the split list
for (String num : delimitedNumbersSplit) {
// remove all whitespaces and parse the number
int n = Integer.parseInt(num.trim());
// add the number to the list of numbers
numberList.add(n);
}
// then create the representation you want to print
StringBuilder sb = new StringBuilder();
// [Java 8] concatenate the numbers to a String that delimits by whitespace
numberList.forEach(number -> sb.append(number).append(" "));
// then remove the trailing whitespace
String numbersWithoutCommas = sb.toString();
numbersWithoutCommas = numbersWithoutCommas.substring(0, numbersWithoutCommas.length() - 1);
// and print the result
System.out.println(numbersWithoutCommas);
}
Note that you don't need to trim() the results of the split String if you have a list without whitespaces.
In case you need the PetitParser library, you will have to look up how to use it in its docs.
With a bit more code I got the result without the delimiters:
Parser number = digit().plus().flatten().trim()
.map((String value) -> Integer.parseInt(value));
Parser next = of(',').seq(number).map((List<?> input) -> input.get(1)).star();
Parser parser = number.seq(next).map((List<List<?>> input) -> {
List<Object> result = new ArrayList<>();
result.add(input.get(0));
result.addAll(input.get(1));
return result;
});

Regex How to match 2 any, but different characters

So I have a String String s = "4433334552223"; that I would like to split into an array, on every character change (between every pair of different of characters). String [] aRay = s.split("IDK"); I'm wanting the String array to contain {44,3333,4,55,222,3} after the split().
I know how to do it with a loop and such, but I was just wondering if there was a simple way to do this with regex??
You can use a backreference to match repeated characters:
String s = "4433334552223";
Matcher m = Pattern.compile("(.)\\1*").matcher(s);
while (m.find()) {
System.out.println(m.group());
}
Ideone Demo
You can use the following code:
String input ="4433334552223";
final String PATTERN = "(.)(\\1*)";
Matcher m = Pattern.compile(PATTERN).matcher(input);
ArrayList<String> result = new ArrayList<String>();
while(m.find())
{
result.add(m.group(1)+m.group(2));
}
System.out.println(result.toString());
This produce the following output:
[44, 3333, 4, 55, 222, 3]

How to split a string into only positive and negative integers?

I'm writing a program to do different calculations with vector functions, but the program I have as of now delimits the negative digits. I've tried using different delimiters but I can't seem to get the right one.
Does anyone know how to keep the positive and negative digits when splitting a string? Also, is there a way to keep any decimal values? .45 would return 45 and .29 would return 29
This is the code:
ArrayList<Integer> list = new ArrayList<Integer>();
String twoVectors = "a=<-1,2,-3> b=<4,-5,6>"; // vector and a and b
String[] tokens = twoVectors.split("\\D");
for (String s : tokens)
if (!s.equals(""))
list.add(Integer.parseInt(s));
System.out.println(Arrays.toString(list.toArray()));
When I run the program I get [1, 2, 3, 4, 5, 6] instead of [-1, 2, -3, 4, -5, 6]. All the functions I have worked perfectly fine but dont work when using negative values.
Any help would be appreciated.
You can use
String[] tokens = twoVectors.split("[^\\d-]+");
[^\\d-]+ : match anything except digits and -
[] : match everything mentioned inside []
^ : negation mean do not match (\\d-)
\\d- : digits 0-9 and - character
Regex Demo
String twoVectors = "a=<-1,2,-3> b=<4,-5,6>";
ArrayList<Integer> list = new ArrayList<Integer>();
String[] tokens = twoVectors.split("[^\\d-]");
for (String s : tokens)
if (!s.equals(""))
list.add(Integer.parseInt(s));
System.out.println(Arrays.toString(list.toArray()));
Output :
[-1, 2, -3, 4, -5, 6]
Or
you can use Pattern along with matcher to find all the desired values i.e singed or unsigned numbers with -?\\d+ regex
Regex Demo -?\d+
Update : For Double values , you can use [^\\d-.]+ and make sure to use Double instead of Integer along with Double.parseDouble
And with Pattern and Matcher use -?\\d*\\.?\\d+
Use [^\\d-] inside your split method i.e. twoVectors.split("[^\\d-]")
Why [^\\d-]:
^ : Finds regex that must match at the beginning of the line.
\d : Any digit from [0-9]
- : will match '-' if it exists
The regex that you currently have splits the string on anything but digits. So anything that is not a digit is considered a splitter. If you added - sign to this pattern, anything that is not a digit or a - sign will be included. This will work for some cases, but will fail if you have - or . without a number afterwards.
What you need to do is to specify the number format in a regex (like -?\d*.?\d+), and then find all matches of this pattern. You will also need to change the numbers to Double so that you can parse decimal numbers.
String twoVectors = "a=<-1,.2,-3> b=<4,-5,6>";
ArrayList<Double> numbers = new ArrayList<Double>();
Matcher matcher = Pattern.compile("-?\\d*\\.?\\d+").matcher(twoVectors);
while (matcher.find()) {
numbers.add(Double.parseDouble(matcher.group()));
}
System.out.println(Arrays.toString(numbers.toArray()));
Output
[-1.0, 0.2, -3.0, 4.0, -5.0, 6.0]
A 1-line solution:
List<Integer> numbers = Arrays
.stream(twoVectors.replaceAll("^[^\\d-]+", "").split("[^\\d-]+"))
.map(Integer::new)
.collect(Collectors.toList());
The initial replace is to remove the leading non-target chars (otherwise the split would return a blank in the first element).

Java - Extract string from pattern

Given some strings that look like this:
(((((((((((((4)+13)*5)/1)+7)+12)*3)-6)-11)+9)*2)/8)-10)
(((((((((((((4)+13)*6)/1)+5)+12)*2)-7)-11)+8)*3)/9)-10)
(((((((((((((4)+13)*6)/1)+7)+12)*2)-8)-11)+5)*3)/9)-10)
(btw, they are solutions for a puzzle which I write a program for :) )
They all share this pattern
"(((((((((((((.)+13)*.)/.)+.)+12)*.)-.)-11)+.)*.)/.)-10)"
For 1 solution : How can I get the values with this given pattern?
So for the first solution I will get an collection,list,array (doesn't matter) like this:
[4,5,1,7,3,6,9,2,8]
You've done most of the work actually by providing the pattern. All you need to do is use capturing groups where the . are (and escape the rest).
I put your inputs in a String array and got the results into a List of integers (as you said, you can change it to something else). As for the pattern, you want to capture the dots; this is done by surrounding them with ( and ). The problem in your case is that the whole string is full of them, so we need to quote / escape them out (meaning, tell the regex compiler that we mean the literal / character ( and )). This can be done by putting the part we want to escape between \Q and \E.
The code below shows a coherent (though maybe not effective) way to do this. Just be careful with using the right amount of \ in the right places:
public class Example {
public static void main(String[] args) {
String[] inputs = new String[3];
inputs[0] = "(((((((((((((4)+13)*5)/1)+7)+12)*3)-6)-11)+9)*2)/8)-10)";
inputs[1] = "(((((((((((((4)+13)*6)/1)+5)+12)*2)-7)-11)+8)*3)/9)-10)";
inputs[2] = "(((((((((((((4)+13)*6)/1)+7)+12)*2)-8)-11)+5)*3)/9)-10)";
List<Integer> results;
String pattern = "(((((((((((((.)+13)*.)/.)+.)+12)*.)-.)-11)+.)*.)/.)-10)"; // Copy-paste from your question.
pattern = pattern.replaceAll("\\.", "\\\\E(.)\\\\Q");
pattern = "\\Q" + pattern;
Pattern p = Pattern.compile(pattern);
Matcher m;
for (String input : inputs) {
m = p.matcher(input);
results = new ArrayList<>();
if (m.matches()) {
for (int i = 1; i < m.groupCount() + 1; i++) {
results.add(Integer.parseInt(m.group(i)));
}
}
System.out.println(results);
}
}
}
Output:
[4, 5, 1, 7, 3, 6, 9, 2, 8]
[4, 6, 1, 5, 2, 7, 8, 3, 9]
[4, 6, 1, 7, 2, 8, 5, 3, 9]
Notes:
You are using a single ., which means
Any character (may or may not match line terminators)
So if you have a number there which is not a single digit or a single character which is not a number (digit), something will go wrong either in the matches or parseInt. Consider \\d to signify a single digit or \\d+ for a number instead.
See Pattern for more info on regex in Java.

How to extract numbers from a string and get an array of ints?

I have a String variable (basically an English sentence with an unspecified number of numbers) and I'd like to extract all the numbers into an array of integers. I was wondering whether there was a quick solution with regular expressions?
I used Sean's solution and changed it slightly:
LinkedList<String> numbers = new LinkedList<String>();
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(line);
while (m.find()) {
numbers.add(m.group());
}
Pattern p = Pattern.compile("-?\\d+");
Matcher m = p.matcher("There are more than -2 and less than 12 numbers here");
while (m.find()) {
System.out.println(m.group());
}
... prints -2 and 12.
-? matches a leading negative sign -- optionally. \d matches a digit, and we need to write \ as \\ in a Java String though. So, \d+ matches 1 or more digits.
What about to use replaceAll java.lang.String method:
String str = "qwerty-1qwerty-2 455 f0gfg 4";
str = str.replaceAll("[^-?0-9]+", " ");
System.out.println(Arrays.asList(str.trim().split(" ")));
Output:
[-1, -2, 455, 0, 4]
Description
[^-?0-9]+
[ and ] delimites a set of characters to be single matched, i.e., only one time in any order
^ Special identifier used in the beginning of the set, used to indicate to match all characters not present in the delimited set, instead of all characters present in the set.
+ Between one and unlimited times, as many times as possible, giving back as needed
-? One of the characters “-” and “?”
0-9 A character in the range between “0” and “9”
Pattern p = Pattern.compile("[0-9]+");
Matcher m = p.matcher(myString);
while (m.find()) {
int n = Integer.parseInt(m.group());
// append n to list
}
// convert list to array, etc
You can actually replace [0-9] with \d, but that involves double backslash escaping, which makes it harder to read.
StringBuffer sBuffer = new StringBuffer();
Pattern p = Pattern.compile("[0-9]+.[0-9]*|[0-9]*.[0-9]+|[0-9]+");
Matcher m = p.matcher(str);
while (m.find()) {
sBuffer.append(m.group());
}
return sBuffer.toString();
This is for extracting numbers retaining the decimal
The accepted answer detects digits but does not detect formated numbers, e.g. 2,000, nor decimals, e.g. 4.8. For such use -?\\d+(,\\d+)*?\\.?\\d+?:
Pattern p = Pattern.compile("-?\\d+(,\\d+)*?\\.?\\d+?");
List<String> numbers = new ArrayList<String>();
Matcher m = p.matcher("Government has distributed 4.8 million textbooks to 2,000 schools");
while (m.find()) {
numbers.add(m.group());
}
System.out.println(numbers);
Output:
[4.8, 2,000]
Using Java 8, you can do:
String str = "There 0 are 1 some -2-34 -numbers 567 here 890 .";
int[] ints = Arrays.stream(str.replaceAll("-", " -").split("[^-\\d]+"))
.filter(s -> !s.matches("-?"))
.mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(ints)); // prints [0, 1, -2, -34, 567, 890]
If you don't have negative numbers, you can get rid of the replaceAll (and use !s.isEmpty() in filter), as that's only to properly split something like 2-34 (this can also be handled purely with regex in split, but it's fairly complicated).
Arrays.stream turns our String[] into a Stream<String>.
filter gets rid of the leading and trailing empty strings as well as any - that isn't part of a number.
mapToInt(Integer::parseInt).toArray() calls parseInt on each String to give us an int[].
Alternatively, Java 9 has a Matcher.results method, which should allow for something like:
Pattern p = Pattern.compile("-?\\d+");
Matcher m = p.matcher("There 0 are 1 some -2-34 -numbers 567 here 890 .");
int[] ints = m.results().map(MatchResults::group).mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(ints)); // prints [0, 1, -2, -34, 567, 890]
As it stands, neither of these is a big improvement over just looping over the results with Pattern / Matcher as shown in the other answers, but it should be simpler if you want to follow this up with more complex operations which are significantly simplified with the use of streams.
for rational numbers use this one: (([0-9]+.[0-9]*)|([0-9]*.[0-9]+)|([0-9]+))
Extract all real numbers using this.
public static ArrayList<Double> extractNumbersInOrder(String str){
str+='a';
double[] returnArray = new double[]{};
ArrayList<Double> list = new ArrayList<Double>();
String singleNum="";
Boolean numStarted;
for(char c:str.toCharArray()){
if(isNumber(c)){
singleNum+=c;
} else {
if(!singleNum.equals("")){ //number ended
list.add(Double.valueOf(singleNum));
System.out.println(singleNum);
singleNum="";
}
}
}
return list;
}
public static boolean isNumber(char c){
if(Character.isDigit(c)||c=='-'||c=='+'||c=='.'){
return true;
} else {
return false;
}
}
Fraction and grouping characters for representing real numbers may differ between languages. The same real number could be written in very different ways depending on the language.
The number two million in German
2,000,000.00
and in English
2.000.000,00
A method to fully extract real numbers from a given string in a language agnostic way:
public List<BigDecimal> extractDecimals(final String s, final char fraction, final char grouping) {
List<BigDecimal> decimals = new ArrayList<BigDecimal>();
//Remove grouping character for easier regexp extraction
StringBuilder noGrouping = new StringBuilder();
int i = 0;
while(i >= 0 && i < s.length()) {
char c = s.charAt(i);
if(c == grouping) {
int prev = i-1, next = i+1;
boolean isValidGroupingChar =
prev >= 0 && Character.isDigit(s.charAt(prev)) &&
next < s.length() && Character.isDigit(s.charAt(next));
if(!isValidGroupingChar)
noGrouping.append(c);
i++;
} else {
noGrouping.append(c);
i++;
}
}
//the '.' character has to be escaped in regular expressions
String fractionRegex = fraction == POINT ? "\\." : String.valueOf(fraction);
Pattern p = Pattern.compile("-?(\\d+" + fractionRegex + "\\d+|\\d+)");
Matcher m = p.matcher(noGrouping);
while (m.find()) {
String match = m.group().replace(COMMA, POINT);
decimals.add(new BigDecimal(match));
}
return decimals;
}
If you want to exclude numbers that are contained within words, such as bar1 or aa1bb, then add word boundaries \b to any of the regex based answers. For example:
Pattern p = Pattern.compile("\\b-?\\d+\\b");
Matcher m = p.matcher("9There 9are more9 th9an -2 and less than 12 numbers here9");
while (m.find()) {
System.out.println(m.group());
}
displays:
2
12
I would suggest to check the ASCII values to extract numbers from a String
Suppose you have an input String as myname12345 and if you want to just extract the numbers 12345 you can do so by first converting the String to Character Array then use the following pseudocode
for(int i=0; i < CharacterArray.length; i++)
{
if( a[i] >=48 && a[i] <= 58)
System.out.print(a[i]);
}
once the numbers are extracted append them to an array
Hope this helps
I found this expression simplest
String[] extractednums = msg.split("\\\\D++");
public static String extractNumberFromString(String number) {
String num = number.replaceAll("[^0-9]+", " ");
return num.replaceAll(" ", "");
}
extracts only numbers from string

Categories