Reputation: 85
How to split a string?
I've got a String
:
String s = "1+2*3(30+4/2-(10+2))*2+1";
Got method which get digit values from String
:
private static DigitStack getNumber(String convert) {
String[] arr = convert.split("");
digitStack = new DigitStack();
try {
for (int i = 0; i < arr.length ; i++) {
while (arr[i].matches("\\d+")) {
digitStack.push(Double.valueOf(arr[i]));
i++;
}
}
}catch (Exception e){
e.printStackTrace();
}
return digitStack;
}
method return values:
1.0; 2.0; 3.0; [3.0 0.0]; 4.0; 2.0; [1.0 0.0]; 2.0; 2.0; 1.0;
expected output:
1.0; 2.0; 3.0; 30.0; 4.0; 2.0; 10.0; 2.0; 2.0; 1.0;
Problem is method count every symbol like separate, and number 30 and 10. It convert in two numbers [3.0 and 0.0] [1.0 and 0.0]
How to fix it?
Upvotes: 3
Views: 398
Reputation: 51445
My answer is a little different because I'm assuming that you'll eventually want the operators as well as the values.
Here's the result from one of my test runs.
Original Equation: 1+2*3(30+4/2-(10+2))*2+1
Expanded Equation: 1 + 2 * 3 ( 30 + 4 / 2 - ( 10 + 2 ) ) * 2 + 1
It looks like all I did was add spaces to the equation. What I actually did was divide the equation into parts, and print each part, followed by a space.
Here's the code I wrote to create a List<String>
of parts from the original equation.
private List<String> parseEquation(String equation) {
List<String> parts = new ArrayList<>();
StringBuilder builder = new StringBuilder();
String testCharacters = "+-*/()";
char[] letters = equation.toCharArray();
for (int i = 0; i < letters.length; i++) {
if (letters[i] == ' ') {
continue;
}
if (Character.isDigit(letters[i])) {
builder.append(letters[i]);
continue;
}
if (letters[i] == '.') {
builder.append(letters[i]);
continue;
}
if (contains(letters[i], testCharacters)) {
addPart(parts, builder);
parts.add(Character.toString(letters[i]));
}
}
addPart(parts, builder);
return parts;
}
private void addPart(List<String> parts, StringBuilder builder) {
if (builder.length() > 0) {
parts.add(builder.toString());
builder.delete(0, builder.length());
}
}
private boolean contains(char c, String text) {
for (int i = 0; i < text.length(); i++) {
char t = text.charAt(i);
if (c == t) {
return true;
}
}
return false;
}
The addPart
method is a utility method that checks for an empty part. If the part is not empty, this method adds the part to the List
.
The contains
method is a utility method that allows me to check for all the operators and parenthesis with one if
statement.
The parseEquation
method doesn't use any fancy regexes. It's straightforward.
I check each character in the equation. If the character is a space, I skip the character. If the character is a digit or a dot, I add it to the StringBuilder
to create a number. If the character is an operator or parenthesis, I add the previous number to the List
of parts, then add the operator or parenthesis to the List
of parts.
I use continue
statements to skip to the bottom of the for
loop and make the code a little neater. I don't have nested if
statements with the continue
statements.
Here's the complete, runnable code. You can see by the commented line that I wrote an equation solver as well. I'll leave that part of the code to you.
import java.util.ArrayList;
import java.util.List;
public class EquationSolver {
private static final String FORMAT = "%-30s";
public static void main(String[] args) {
String equation = "1+2*3(30+4/2-(10+2))*2+1";
String title = String.format(FORMAT, "Original Equation:");
System.out.println(title + equation);
EquationSolver es = new EquationSolver();
List<String> parts = es.parseEquation(equation);
// es.processEquation(parts);
title = String.format(FORMAT, "Expanded Equation:");
System.out.println(title + es.toString(parts));
}
private List<String> parseEquation(String equation) {
List<String> parts = new ArrayList<>();
StringBuilder builder = new StringBuilder();
String testCharacters = "+-*/()";
char[] letters = equation.toCharArray();
for (int i = 0; i < letters.length; i++) {
if (letters[i] == ' ') {
continue;
}
if (Character.isDigit(letters[i])) {
builder.append(letters[i]);
continue;
}
if (letters[i] == '.') {
builder.append(letters[i]);
continue;
}
if (contains(letters[i], testCharacters)) {
addPart(parts, builder);
parts.add(Character.toString(letters[i]));
}
}
addPart(parts, builder);
return parts;
}
private void addPart(List<String> parts, StringBuilder builder) {
if (builder.length() > 0) {
parts.add(builder.toString());
builder.delete(0, builder.length());
}
}
private boolean contains(char c, String text) {
for (int i = 0; i < text.length(); i++) {
char t = text.charAt(i);
if (c == t) {
return true;
}
}
return false;
}
private String toString(List<String> parts) {
StringBuilder builder = new StringBuilder();
for (String s : parts) {
builder.append(s).append(" ");
}
return builder.toString().trim();
}
}
Upvotes: 0
Reputation: 40034
This will get both integer and floating point values assuming that are syntactically correct. Operators, parens, and variables are ignored.
String str = "1.9+.2*3(30.2+4./2-(10.23+2))*2+1";
String[] digits = str.split("[^\\d\\.]+");
System.out.println(Arrays.toString(digits));
Prints
[1.9, .2, 3, 30.2, 4., 2, 10.23, 2, 2, 1]
Upvotes: 0
Reputation: 13715
String[] digits = Pattern.compile("\\d+")
.matcher("1+2*3(30+4/2-(10+2))*2+1")
.results()
.map(MatchResult::group)
.toArray(String[]::new);
\d+
in regex refers to one or more digits.
This returns the array:
{ "1", "2", "3", "30", "4", "2", "10", "2", "2", "1" }
If you want to convert this to a number array:
double[] matches = Arrays.stream(digits)
.mapToDouble(Float::parseFloat)
.toArray();
Upvotes: 0
Reputation: 535
Assuming you are using Java 9 you can simply get all matches for the regex and convert them to double.
import java.util.regex.Pattern;
import java.util.regex.MatchResult;
private static DigitStack getNumber(String convert) {
digitStack = new DigitStack();
try {
String numbers[] = Pattern.compile("\\d(\\d+|\\.)+")
.matcher(convert)
.results()
.map(MatchResult::group)
.toArray(String[]::new);
for(String number : numbers ) digitStack.push(Double.valueOf(number));
}catch (Exception e){
e.printStackTrace();
}
return digitStack;
}
The problem with your code was that you were checking every character separately. You have to get all numbers first then convert them to double.
Upvotes: 0
Reputation: 2282
Simplest way would be to split your string on non-digits i.e.
String s = "1+2*3(30+4/2-(10+2))*2+1";
String[] arr = s.split("\\D+");
This will return an array
[1, 2, 3, 30, 4, 2, 10, 2, 2, 1]
which you can then iterate through and convert to double.
As Rohit pointed out this approach will not work if you have numbers with decimal points or if you want to consider also negative numbers.
Upvotes: 2