Need help with . string.substring(0, max.Length) - c#

ArgumentOutofBounds Exceptions is thrown all the times inside the ifs in the loop.
In this code I am trying to send two strings between the symbol #2#3.
string1+"#2#3"+string2
Now I try to separate the strings from the symbols by the method substring, but an exception is being thrown over there......
public void seperatePMChattersNames(string TwoNames)
{
string nameOne="";
string nameTwo="";
Console.WriteLine(TwoNames);
for (int i = 0; i < TwoNames.Length; i++)
{
if (TwoNames[i] == '2' && TwoNames[i-1] == '#')///ArgumentOutofRange Exception
{
nameOne = TwoNames.Substring(0, i);
}
if (TwoNames[i] == '#' && TwoNames[i+1] == '3')///ArgumentOutofRange Exception
{
nameTwo = TwoNames.Substring(i+1, TwoNames.Length);
}
}
}
Why is it thrown and how to prevent it?

When i is zero, TwoNames[i - 1] will try to access index -1 of the string - that doesn't exist.
When i is TwoNames.Length - 1, TwoNames[i + 1] will try to access past the end of the string.
Next, when you have found "#3", you're using:
TwoNames.Substring(i+1, TwoNames.Length)
the second parameter of Substring is the length of the substring to take, not the final index. If you just want the rest of the string, you can omit the second argument:
TwoNames.Substring(i+1)
Note that that would include the "3" though - so you probably really want i+2 instead of i+1.
Is there any reason you're not using string.IndexOf(TwoNames, "#2") etc?
If you just want nameOne to be the string before the first "#2" and nameTwo to be the string after the last "#3", you can use:
int endOfOne = TwoNames.IndexOf("#2");
if (endOfOne != -1)
{
nameOne = TwoNames.Substring(0, endOfOne);
}
else
{
// Couldn't find #2... throw exception perhaps?
}
int startOfTwo = TwoNames.LastIndexOf("#3");
if (startOfTwo != -1)
{
// Allow for the "#3" itself
nameTwo = TwoNames.Substring(startOfTwo + 2);
}
else
{
// Couldn't find #3... throw exception perhaps?
}
Another option is to use regular expressions, of course - they add a degree of complexity themselves, but they really are aimed at pattern matching. If you find you need to get even more bits and pieces, they could help.

Your loop starts with i = 0 but in your first if-statement you try access TwoNames[i-1] which tries to access TwoNames[-1]. Clearly this will cause an issue since there is no TwoName[-1].
Your loop should start with i = 1, not 0.

for (int i = 0; i < TwoNames.Length; i++)
should be
for (int i = 1; i < TwoNames.Length-1; i++)

This is the answer
public void seperatePMChattersNames(string TwoNames)
{
Console.WriteLine(TwoNames);
int x = TwoNames.IndexOf("#2");
int y = TwoNames.IndexOf("#3");
string nameone = TwoNames.Substring(0, x);
string nametwo = TwoNames.Substring(y+2);
Console.WriteLine(nameone);
Console.WriteLine(nametwo);
}

Related

Splitting the letters based on position of letter

I am trying to spilt a string word into two strings based on the letter position. The two strings are even and odd. I manage to read the string and used a for loop but the conditional operator is not working and give me the error below. What did I do wrong?
Example: The string word is pole
Even position string - oe
Odd position string - pl
Error
Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
string word = Console.ReadLine();
for(int i = 0; i < word.Length; i++)
{
string even = "";
string odd = "";
((i % 2 == 0) ? even += word[i]: odd += word[i]);
}
You could use the discard operator as the following.
string word = Console.ReadLine();
string even = "";
string odd = "";
for(int i = 0; i < word.Length; i++)
{
var _ = ((i % 2 == 0) ? even += word[i]: odd += word[i]);
}
Couple of points to note here.
You need to declare the odd,even variables outside the loop, otherwise it would be recreated for each iteration of loop
Remember the string is immutable.You could also consider the StringBuilder class.
I am not that familiar with the ? operator, however, in my research, it appears it wants something like below…
((i % 2 == 0) ? ref even : ref odd) += word[i];
Unfortunately, even with this change, the even and odd variables are getting “reset” to empty with each iteration of the for loop with…
string even = "";
string odd = "";
If the goal is to concatenate the values, you do NOT want to create new even and odd variables with each iteration. So you should move those declarations “outside” the for loop. Something like…
string word = Console.ReadLine();
string even = "";
string odd = "";
for (int i = 0; i < word.Length; i++) {
((i % 2 == 0) ? ref even : ref odd) += word[i];
}
You use conditional operator to assign values inside of it. It is not allowed.
The correct for-loop is:
for (int i = 0; i < word.Length; i++)
{
if (i % 2 == 0)
{
even += word[i];
}
else
{
odd += word[i];
};
}
You can also use LINQ to get the expected result:
string word = Console.ReadLine();
string even = string.Concat(word.Where((c,i) => i % 2 == 0));
string odd = string.Concat(word.Where((c,i) => i % 2 == 1));
Online demo: https://dotnetfiddle.net/ePWHnp

Is it possible to `continue` a foreach loop while in a for loop?

Okay, so what I want to do should sound pretty simple. I have a method that checks every character in a string if it's a letter from a to m. I now have to continue a foreach loop, while in a for loop. Is there a possible way to do what I want to do?
public static string Function(String s)
{
int error = 0;
foreach (char c in s)
{
for (int i = 97; i <= 109; i++)
{
if (c == (char)i)
{
// Here immediately continue the upper foreach loop, not the for loop
continue;
}
}
error++;
}
int length = s.Length;
return error + "/" + length;
}
If there's a character that's not in the range of a to m, there should be 1 added to error. In the end, the function should return the number of errors and the number of total characters in the string, f.E: "3/17".
Edit
What I wanted to achieve is not possible. There are workarounds, demonstrated in BsdDaemon's answer, by using a temporary variable.
The other answers fix my issue directly, by simply improving my code.
i think ('a' == (char)i) should be (c == (char)i) .
and why not replace the for with just if((int)c >= 97 && (int)c <= 109)?
you solution might work but is extremly unperformant
How about using LINQ:
int errors = s
.Count(c => !Enumerable.Range(97, 13).Contains(c));
Then there is no need to break out of the loop.
Or to avoid the nested loop altogether, which will improve performance:
int errors = s.Count(c => c < 97 || c > 109);
char is implicitly convertible to int so there's no need to cast.
You can do this by breaking the internal loop, this means the internal loop will be escaped as if the iterations ended. After this, you can use a boolean to control with continue if the rest of the underlying logic processes:
public static string Function(String s)
{
int error = 0;
foreach (char c in s)
{
bool skip = false;
for (int i = 97; i <= 109; i++)
{
if ('a' == (char)i)
{
skip = true;
break;
}
}
if (skip) continue;
error++;
}
string length = Convert.ToString(s.Length);
return error + "/" + length;
}
Regular Expressions are perfectly suited to handle this type of "problem" and is considerably more flexible...for one thing you would not be limited to consecutive characters. The following console app demonstrates how to use Regex to extract the desired information from the targeted string.
private static string TestSearchTextRegex(string textToSearch)
{
var pattern = "[^a-m]";
var ms = Regex.Matches(textToSearch, pattern);
return $"{ms.Count}/{textToSearch.Length}";
}
NOTE
The pattern "[^a-m]" basically says: find a match that is NOT (^) in the set of characters. This pattern could easily be defined as "[^a-mz]" which in addition to characters "a-m" would also consider "z" to also be a character that would not be counted in the error group.
Another advantage to the Regex solution, you are able to use the actual characters you are looking for as apposed to the number that represents that character.
The continue will skip the further lines in that iterations and if you need to break out the loop use break.
public static string Function(String s)
{
int error = 0;
foreach (char c in s)
{
for (int i = 97; i <= 109; i++)
{
if ('a' == (char)i)
{
break; // break the whole looping, continue will break only the iteration
}
}
error++;
}
string length = Convert.ToString(s.Length);
return error + "/" + length;
}

Why does my bubblesort code need - 2 after the structure.Length for it to work?

I am trying to understand why my productTable.Length has to be - 2 for my bubblesort code to work.
I created two int variables, Last_Position and i. I created one product variable called temp and one bool called swap which is set to false. I then set Last_Position to equal productTable.Length - 2.
This is where I fail to understand, from what I have read the .Length counts the amount of characters and returns the amount however since 1 counts as 0 in programming, you have to - 1 to have the cap be accurate (i.e 1000 = 999) which has remained true until this part.
For some reason - 1 will throw up an error when the program runs targeting this code: if (String.Compare(productTable[i].prodCode, productTable[i + 1].prodCode) > 0) and states "System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'"
The code works when I set it to - 2 but I want to understand why that is.
struct product
{
public string prodCode;
public string description;
public double price;
public int quantity;
}
product[] productTable;
public void loadData()
{
string path = "C:\\Users\\5004303\\Documents\\productFile.csv";
int lineCount = File.ReadLines(path).Count();
productTable = new product[lineCount];
product currentProduct = new product();
try
{
StreamReader sr = new StreamReader(path);
string line;
int currentArrayLocation = 0;
while (!sr.EndOfStream)
{
line = sr.ReadLine();
string[] fields = line.Split(',');
currentProduct.prodCode = fields[0];
currentProduct.description = fields[1];
currentProduct.price = Convert.ToDouble(fields[2]);
currentProduct.quantity = Convert.ToInt32(fields[3]);
productTable[currentArrayLocation] = currentProduct;
currentArrayLocation++;
}
sr.Close();
}
catch (FileNotFoundException)
{
MessageBox.Show("An error occured. Could not find file 'productFile.csv'.");
}
}
public void listProducts()
{
int currentArrayLocation = 0;
for (currentArrayLocation = 0; currentArrayLocation < productTable.Length; currentArrayLocation++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = productTable[currentArrayLocation].prodCode;
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].description));
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].price));
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].quantity));
lvProducts.Items.Add(lvi);
}
}
public void bubbleSort()
{
int last_Postion, i;
product temp;
last_Postion = productTable.Length - 2;
Boolean swap = false;
do
{
swap = false;
for (i = 0; i <= last_Postion; i++)
{
if (String.Compare(productTable[i].prodCode, productTable[i + 1].prodCode) > 0)
{
temp = productTable[i];
productTable[i] = productTable[i + 1];
productTable[i + 1] = temp;
swap = true;
}
}
}
while (swap == true);
}
Short answer:
Change
productTable.Lenght - 2 to productTable.Lenght - 1
and
for (i = 0; i <= last_Postion; i++) to for (i = 0; i < last_Postion; i++)
Explanation:
productTable.Lenght gives you the lenght of the list so productTable.Lenght - 1 is the last position in the list (0 to productTable.Lenght - 1).
In your "bubble" for loop inside the while you test against i+1 so i should only go up to the last_position - 1.
In your code when i == last_position then i + 1 is beyond the last position in the list.
Note: I did not check your code for validity, even if you make these changes, there may be other bugs.
Note on style, C# coding guidelines usually specify camel case for variable names, it is better to use lastPosition instead of last_Position. There are other styling "errors" in your code, such as declaring variables at the top of the function, using types instead of var. It may be some of this "errors" are course requirements, but a short read of any coding conventions document (e.g. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) would be beneficial to you. Most work places have their own coding guidelines or adopt a public one, but on all of them are pretty similar.

How to get all permutations of groups in a string?

This is not homework, although it may seem like it. I've been browsing through the UK Computing Olympiad's website and found this problem (Question 1): here. I was baffled by it, and I'd want to see what you guys thought of how to do it. I can't think of any neat ways to get everything into groups (checking whether it's a palindrome after that is simple enough, i.e. originalString == new String(groupedString.Reverse.SelectMany(c => c).ToArray), assuming it is a char array).
Any ideas? Thanks!
Text for those at work:
A palindrome is a word that shows the same sequence of letters when
reversed. If a word can have its letters grouped together in two or
more blocks (each containing one or more adjacent letters) then it is
a block palindrome if reversing the order of those blocks results in
the same sequence of blocks.
For example, using brackets to indicate blocks, the following are
block palindromes:
• BONBON can be grouped together as (BON)(BON);
• ONION can be grouped together as (ON)(I)(ON);
• BBACBB can be grouped together as (B)(BACB)(B) or (BB)(AC)(BB) or
(B)(B)(AC)(B)(B)
Note that (BB)(AC)(B)(B) is not valid as the reverse (B)(B)(AC)(BB)
shows the blocks in a different order.
And the question is essentially how to generate all of those groups, to then check whether they are palindromes!
And the question is essentially how to generate all of those groups, to then check whether they are palindromes!
I note that this is not necessarily the best strategy. Generating all the groups first and then checking to see if they are palidromes is considerably more inefficient than generating only those groups which are palindromes.
But in the spirit of answering the question asked, let's solve the problem recursively. I will just generate all the groups; checking whether a set of groups is a palindrome is left as an exercise. I am also going to ignore the requirement that a set of groups contains at least two elements; that is easily checked.
The way to solve this problem elegantly is to reason recursively. As with all recursive solutions, we begin with a trivial base case:
How many groupings are there of the empty string? There is only the empty grouping; that is, the grouping with no elements in it.
Now we assume that we have a solution to a smaller problem, and ask "if we had a solution to a smaller problem, how could we use that solution to solve a larger problem?"
OK, suppose we have a larger problem. We have a string with 6 characters in it and we wish to produce all the groupings. Moreover, the groupings are symmetrical; the first group is the same size as the last group. By assumption we know how to solve the problem for any smaller string.
We solve the problem as follows. Suppose the string is ABCDEF. We peel off A and F from both ends, we solve the problem for BCDE, which remember we know how to do by assumption, and now we prepend A and append F to each of those solutions.
The solutions for BCDE are (B)(C)(D)(E), (B)(CD)(E), (BC)(DE), (BCDE). Again, we assume as our inductive hypothesis that we have the solution to the smaller problem. We then combine those with A and F to produce the solutions for ABCDEF: (A)(B)(C)(D)(E)(F), (A)(B)(CD)(E)(F), (A)(BC)(DE)(F) and (A)(BCDE)(F).
We've made good progress. Are we done? No. Next we peel off AB and EF, and recursively solve the problem for CD. I won't labour how that is done. Are we done? No. We peel off ABC and DEF and recursively solve the problem for the empty string in the middle. Are we done? No. (ABCDEF) is also a solution. Now we're done.
I hope that sketch motivates the solution, which is now straightforward. We begin with a helper function:
public static IEnumerable<T> AffixSequence<T>(T first, IEnumerable<T> body, T last)
{
yield return first;
foreach (T item in body)
yield return item;
yield return last;
}
That should be easy to understand. Now we do the real work:
public static IEnumerable<IEnumerable<string>> GenerateBlocks(string s)
{
// The base case is trivial: the blocks of the empty string
// is the empty set of blocks.
if (s.Length == 0)
{
yield return new string[0];
yield break;
}
// Generate all the sequences for the middle;
// combine them with all possible prefixes and suffixes.
for (int i = 1; s.Length >= 2 * i; ++i)
{
string prefix = s.Substring(0, i);
string suffix = s.Substring(s.Length - i, i);
string middle = s.Substring(i, s.Length - 2 * i);
foreach (var body in GenerateBlocks(middle))
yield return AffixSequence(prefix, body, suffix);
}
// Finally, the set of blocks that contains only this string
// is a solution.
yield return new[] { s };
}
Let's test it.
foreach (var blocks in GenerateBlocks("ABCDEF"))
Console.WriteLine($"({string.Join(")(", blocks)})");
The output is
(A)(B)(C)(D)(E)(F)
(A)(B)(CD)(E)(F)
(A)(BC)(DE)(F)
(A)(BCDE)(F)
(AB)(C)(D)(EF)
(AB)(CD)(EF)
(ABC)(DEF)
(ABCDEF)
So there you go.
You could now check to see whether each grouping is a palindrome, but why? The algorithm presented above can be easily modified to eliminate all non-palindromes by simply not recursing if the prefix and suffix are unequal:
if (prefix != suffix) continue;
The algorithm now enumerates only block palindromes. Let's test it:
foreach (var blocks in GenerateBlocks("BBACBB"))
Console.WriteLine($"({string.Join(")(", blocks)})");
The output is below; again, note that I am not filtering out the "entire string" block but doing so is straightforward.
(B)(B)(AC)(B)(B)
(B)(BACB)(B)
(BB)(AC)(BB)
(BBACBB)
If this subject interests you, consider reading my series of articles on using this same technique to generate every possible tree topology and every possible string in a language. It starts here:
http://blogs.msdn.com/b/ericlippert/archive/2010/04/19/every-binary-tree-there-is.aspx
This should work:
public List<string> BlockPalin(string s) {
var list = new List<string>();
for (int i = 1; i <= s.Length / 2; i++) {
int backInx = s.Length - i;
if (s.Substring(0, i) == s.Substring(backInx, i)) {
var result = string.Format("({0})", s.Substring(0, i));
result += "|" + result;
var rest = s.Substring(i, backInx - i);
if (rest == string.Empty) {
list.Add(result.Replace("|", rest));
return list;
}
else if (rest.Length == 1) {
list.Add(result.Replace("|", string.Format("({0})", rest)));
return list;
}
else {
list.Add(result.Replace("|", string.Format("({0})", rest)));
var recursiveList = BlockPalin(rest);
if (recursiveList.Count > 0) {
foreach (var recursiveResult in recursiveList) {
list.Add(result.Replace("|", recursiveResult));
}
}
else {
//EDIT: Thx to #juharr this list.Add is not needed...
// list.Add(result.Replace("|",string.Format("({0})",rest)));
return list;
}
}
}
}
return list;
}
And call it like this (EDIT: Again thx to #juharr, the distinct is not needed):
var x = BlockPalin("BONBON");//.Distinct().ToList();
var y = BlockPalin("ONION");//.Distinct().ToList();
var z = BlockPalin("BBACBB");//.Distinct().ToList();
The result:
x contains 1 element: (BON)(BON)
y contains 1 element: (ON)(I)(ON)
z contains 3 elements: (B)(BACB)(B),(B)(B)(AC)(B)(B) and (BB)(AC)(BB)
Although not so elegant as the one provided by #Eric Lippert, one might find interesting the following iterative string allocation free solution:
struct Range
{
public int Start, End;
public int Length { get { return End - Start; } }
public Range(int start, int length) { Start = start; End = start + length; }
}
static IEnumerable<Range[]> GetPalindromeBlocks(string input)
{
int maxLength = input.Length / 2;
var ranges = new Range[maxLength];
int count = 0;
for (var range = new Range(0, 1); ; range.End++)
{
if (range.End <= maxLength)
{
if (!IsPalindromeBlock(input, range)) continue;
ranges[count++] = range;
range.Start = range.End;
}
else
{
if (count == 0) break;
yield return GenerateResult(input, ranges, count);
range = ranges[--count];
}
}
}
static bool IsPalindromeBlock(string input, Range range)
{
return string.Compare(input, range.Start, input, input.Length - range.End, range.Length) == 0;
}
static Range[] GenerateResult(string input, Range[] ranges, int count)
{
var last = ranges[count - 1];
int midLength = input.Length - 2 * last.End;
var result = new Range[2 * count + (midLength > 0 ? 1 : 0)];
for (int i = 0; i < count; i++)
{
var range = result[i] = ranges[i];
result[result.Length - 1 - i] = new Range(input.Length - range.End, range.Length);
}
if (midLength > 0)
result[count] = new Range(last.End, midLength);
return result;
}
Test:
foreach (var input in new [] { "BONBON", "ONION", "BBACBB" })
{
Console.WriteLine(input);
var blocks = GetPalindromeBlocks(input);
foreach (var blockList in blocks)
Console.WriteLine(string.Concat(blockList.Select(range => "(" + input.Substring(range.Start, range.Length) + ")")));
}
Removing the line if (!IsPalindromeBlock(input, range)) continue; will produce the answer to the OP question.
It's not clear if you want all possible groupings, or just a possible grouping. This is one way, off the top-of-my-head, that you might get a grouping:
public static IEnumerable<string> GetBlocks(string testString)
{
if (testString.Length == 0)
{
yield break;
}
int mid = testString.Length / 2;
int i = 0;
while (i < mid)
{
if (testString.Take(i + 1).SequenceEqual(testString.Skip(testString.Length - (i + 1))))
{
yield return new String(testString.Take(i+1).ToArray());
break;
}
i++;
}
if (i == mid)
{
yield return testString;
}
else
{
foreach (var block in GetBlocks(new String(testString.Skip(i + 1).Take(testString.Length - (i + 1) * 2).ToArray())))
{
yield return block;
}
}
}
If you give it bonbon, it'll return bon. If you give it onion it'll give you back on, i. If you give it bbacbb, it'll give you b,b,ac.
Here's my solution (didn't have VS so I did it using java):
int matches = 0;
public void findMatch(String pal) {
String st1 = "", st2 = "";
int l = pal.length() - 1;
for (int i = 0; i < (pal.length())/2 ; i ++ ) {
st1 = st1 + pal.charAt(i);
st2 = pal.charAt(l) + st2;
if (st1.equals(st2)) {
matches++;
// DO THE SAME THING FOR THE MATCH
findMatch(st1);
}
l--;
}
}
The logic is pretty simple. I made two array of characters and compare them to find a match in each step. The key is you need to check the same thing for each match too.
findMatch("bonbon"); // 1
findMatch("bbacbb"); // 3
What about something like this for BONBON...
string bonBon = "BONBON";
First check character count for even or odd.
bool isEven = bonBon.Length % 2 == 0;
Now, if it is even, split the string in half.
if (isEven)
{
int halfInd = bonBon.Length / 2;
string firstHalf = bonBon.Substring(0, halfInd );
string secondHalf = bonBon.Substring(halfInd);
}
Now, if it is odd, split the string into 3 string.
else
{
int halfInd = (bonBon.Length - 1) / 2;
string firstHalf = bonBon.Substring(0, halfInd);
string middle = bonBon.Substring(halfInd, bonBon.Length - halfInd);
string secondHalf = bonBon.Substring(firstHalf.Length + middle.length);
}
May not be exactly correct, but it's a start....
Still have to add checking if it is actually a palindrome...
Good luck!!

Parsing strings recursively

I am trying to extract information out of a string - a fortran formatting string to be specific. The string is formatted like:
F8.3, I5, 3(5X, 2(A20,F10.3)), 'XXX'
with formatting fields delimited by "," and formatting groups inside brackets, with the number in front of the brackets indicating how many consecutive times the formatting pattern is repeated. So, the string above expands to:
F8.3, I5, 5X, A20,F10.3, A20,F10.3, 5X, A20,F10.3, A20,F10.3, 5X, A20,F10.3, A20,F10.3, 'XXX'
I am trying to make something in C# that will expand a string that conforms to that pattern. I have started going about it with lots of switch and if statements, but am wondering if I am not going about it the wrong way?
I was basically wondering if some Regex wizzard thinks that Regular expressions can do this in one neat-fell swoop? I know nothing about regular expressions, but if this could solve my problem I am considering putting in some time to learn how to use them... on the other hand if regular expressions can't sort this out then I'd rather spend my time looking at another method.
This has to be doable with Regex :)
I've expanded my previous example and it test nicely with your example.
// regex to match the inner most patterns of n(X) and capture the values of n and X.
private static readonly Regex matcher = new Regex(#"(\d+)\(([^(]*?)\)", RegexOptions.None);
// create new string by repeating X n times, separated with ','
private static string Join(Match m)
{
var n = Convert.ToInt32(m.Groups[1].Value); // get value of n
var x = m.Groups[2].Value; // get value of X
return String.Join(",", Enumerable.Repeat(x, n));
}
// expand the string by recursively replacing the innermost values of n(X).
private static string Expand(string text)
{
var s = matcher.Replace(text, Join);
return (matcher.IsMatch(s)) ? Expand(s) : s;
}
// parse a string for occurenses of n(X) pattern and expand then.
// return the string as a tokenized array.
public static string[] Parse(string text)
{
// Check that the number of parantheses is even.
if (text.Sum(c => (c == '(' || c == ')') ? 1 : 0) % 2 == 1)
throw new ArgumentException("The string contains an odd number of parantheses.");
return Expand(text).Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
I would suggest using a recusive method like the example below( not tested ):
ResultData Parse(String value, ref Int32 index)
{
ResultData result = new ResultData();
Index startIndex = index; // Used to get substrings
while (index < value.Length)
{
Char current = value[index];
if (current == '(')
{
index++;
result.Add(Parse(value, ref index));
startIndex = index;
continue;
}
if (current == ')')
{
// Push last result
index++;
return result;
}
// Process all other chars here
}
// We can't find the closing bracket
throw new Exception("String is not valid");
}
You maybe need to modify some parts of the code, but this method have i used when writing a simple compiler. Although it's not completed, just a example.
Personally, I would suggest using a recursive function instead. Every time you hit an opening parenthesis, call the function again to parse that part. I'm not sure if you can use a regex to match a recursive data structure.
(Edit: Removed incorrect regex)
Ended up rewriting this today. It turns out that this can be done in one single method:
private static string ExpandBrackets(string Format)
{
int maxLevel = CountNesting(Format);
for (int currentLevel = maxLevel; currentLevel > 0; currentLevel--)
{
int level = 0;
int start = 0;
int end = 0;
for (int i = 0; i < Format.Length; i++)
{
char thisChar = Format[i];
switch (Format[i])
{
case '(':
level++;
if (level == currentLevel)
{
string group = string.Empty;
int repeat = 0;
/// Isolate the number of repeats if any
/// If there are 0 repeats the set to 1 so group will be replaced by itself with the brackets removed
for (int j = i - 1; j >= 0; j--)
{
char c = Format[j];
if (c == ',')
{
start = j + 1;
break;
}
if (char.IsDigit(c))
repeat = int.Parse(c + (repeat != 0 ? repeat.ToString() : string.Empty));
else
throw new Exception("Non-numeric character " + c + " found in front of the brackets");
}
if (repeat == 0)
repeat = 1;
/// Isolate the format group
/// Parse until the first closing bracket. Level is decremented as this effectively takes us down one level
for (int j = i + 1; j < Format.Length; j++)
{
char c = Format[j];
if (c == ')')
{
level--;
end = j;
break;
}
group += c;
}
/// Substitute the expanded group for the original group in the format string
/// If the group is empty then just remove it from the string
if (string.IsNullOrEmpty(group))
{
Format = Format.Remove(start - 1, end - start + 2);
i = start;
}
else
{
string repeatedGroup = RepeatString(group, repeat);
Format = Format.Remove(start, end - start + 1).Insert(start, repeatedGroup);
i = start + repeatedGroup.Length - 1;
}
}
break;
case ')':
level--;
break;
}
}
}
return Format;
}
CountNesting() returns the highest level of bracket nesting in the format statement, but could be passed in as a parameter to the method. RepeatString() just repeats a string the specified number of times and substitutes it for the bracketed group in the format string.

Categories