C#: String divided into substrings, excluding tabs - c#

I have a file that looks something like this:
0 4 5
6 9 9
etc, with three numbers in each row, separated by tabs. I have an array of the lines from the file:
string[] lines = File.ReadAllLines(theFile);
For each line, I am calling a function whose intended result is to return an array of integers. Here's what my code looks like as of now:
public int[] getPoint(string line)
{
int xlength= line.IndexOf(" "); //find location of first white space
int x= Int32.Parse(line.Substring(0, xlength)); //find x-coordinate and turn it from a string to an int
int ylength = line.IndexOf(" ", xlength); //find next white space
int y = Int32.Parse(line.Substring(xlength + 1, ylength)); //y-coordinate starts after first white space and ends before next
int z = Int32.Parse(line.Substring(ylength + 1, line.Length)); //z starts after 2nd white space and goes through end of line
return new int[] { x, y, z }; //return a new point!
}
My problem is with the IndexOf(string) function. It does not recognize tabs. How could I write this so that each my getPoint function achieves its purpose? Thank you.

Try to use IndexOf() with tabs, not with spaces:
int xlength= line.IndexOf("\t"); //"\t" - to find tabs
Also, use "\t" in every IndexOf() call.

How about this using Split and Linq and LinqPad
void Main()
{
var lines = new List<string> { "0\t4 5", "6\t9\t9" };
lines.Select(l => GetPoint(l)).Dump();
}
static int[] GetPoint(string s)
{
var values = s.Split(new[] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
return values.Select(v => int.Parse(v)).ToArray();
}
Of course you would use
File.ReadLines("path").Select(l => GetPoint(l)).Dump();

Try using the ReadLines method instead. RealLines will read the file line by line into memory as requested. Also, try using the Split method. I would also add error handling in case the found string is not parseable to an int. Here is some code to guide you but you may need to adjust it to fit your needs:
foreach (var thisLine in File.ReadLines(theFile))
{
int[] point = getPoint(thisLine);
}
public int[] getPoint(string line)
{
// Split by tab or space
string[] portions = line.Split(new char[] { ' ', '\t' });
int x = Int32.Parse(portions[0]); //find x-coordinate and turn it from a string to an int
int y = Int32.Parse(portions[1]); //y-coordinate starts after first white space and ends before next
int z = Int32.Parse(portions[2]); //z starts after 2nd white space and goes through end of line
return new int[] { x, y, z }; //return a new point!
}

To add a bit of additional capacity to the suggestions above, you could use Regex.Matches() as follows:
string line = "1\t-25\t5";
var numbers = Regex.Matches(line, #"-?\d+");
foreach (var number in numbers)
Console.WriteLine(number);
And then parse those numbers as integers.
This has two benefits.
It can handle negative numbers, and
It will pull out the numbers successfully, even if tabs weren't used or are not reliable.

Related

Maximum product of 13 adjacent numbers

List<int> arr = new List<int>();
long max = 0;
long mul = 1;
string abc = #"73167176531330624919225119674426574742355349194934
85861560789112949495459501737958331952853208805511
96983520312774506326239578318016984801869478851843
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450";
foreach (char a in abc)
{
if(arr.Count == 13)
{
arr.RemoveAt(0);
}
int value = (int)Char.GetNumericValue(a);
arr.Add(value);
if(arr.Count == 13)
{
foreach(int b in arr)
{
mul = mul * b;
if (mul > max)
{
max = mul;
}
}
mul = 1;
}
}
Console.WriteLine(max);
I am getting 5377010688 which is a wrong answer and when I am trying same logic with given example in project Euler it is working fine, please help me.
Don't say the answer just correct me where I am doing wrong or where the code is not running as it should.
The string constant, as it is written down like above, contains blanks and \r\n's, e.g. between the last '4' of the first line and the first '8' on the second line. Char.GetNumericValue() returns -1 for a blank.
Propably the character sequence with the highest product spans across adjacent lines in your string, therefore there are blanks in between, which count as -1, which disables your code in finding them.
Write your constant like this:
string abc = #"73167176531330624919225119674426574742355349194934" +
"85861560789112949495459501737958331952853208805511" +
"96983520312774506326239578318016984801869478851843" + etc.
The result is then 23514624000, I hope that's correct.
Don't say the answer just correct me where I am doing wrong or where
the code is not running as it should
You have included all characters into calculation but you should not do that. The input string also contains for example carriage return '\n' at the end of each line.
Your actual string look like this:
string abc = #"73167176531330624919225119674426574742355349194934\r\n
85861560789112949495459501737958331952853208805511\r\n
...
How to solve this? You should ignore these characters, one possible solution is to check each char if it is a digit:
if(!char.IsDigit(a))
{
continue;
}

Rearranging a string of numbers from highest to lowest C#

What I'm trying to do is to create a function that will rearrange a string of numbers like "1234" to "4321". I'm certain that there are many much more efficient ways to do this than my method but I just want to see what went wrong with what I did because I'm a beginner at programming and can use the knowledge to get better.
My thought process for the code was to:
find the largest number in the inputted string
add the largest number into a list
remove the largest number from the inputted string
find the largest number again from the (now shorter) string
So I made a function that found the largest number in a string and it worked fine:
static int LargestNumber(string num)
{
int largestnumber = 0;
char[] numbers = num.ToCharArray();
foreach (var number in numbers)
{
int prevNumber = (int) char.GetNumericValue(number);
if (prevNumber >= largestnumber)
{
largestnumber = prevNumber;
}
}
return largestnumber;
}
Now the rearranging function is what I am having problems with:
static List<int> Rearrange(string num)
{
List<int> rearranged = new List<int>(); // to store rearranged numbers
foreach (var number in num) //for every number in the number string
{
string prevnumber = number.ToString(); // the previous number in the loop
if (prevnumber == LargestNumber(num).ToString()) // if the previous number is the larges number in the inputted string (num)
{
rearranged.Add(Convert.ToInt32(prevnumber)); // put the previous number into the list
// removing the previous number (largest) from the inputted string and update the inputted string (which should be now smaller)
StringBuilder sb = new StringBuilder(num);
sb.Remove(num.IndexOf(number), 1);
num = sb.ToString();
}
}
return rearranged; // return the final rearranged list of numbers
}
When I run this code (fixed for concatenation):
var rearranged = Rearrange("3250");
string concat = String.Join(" ", rearranged.ToArray());
Console.WriteLine(concat);
All I get is:
5
I'm not sure what I'm missing or what I'm doing wrong - the code doesn't seem to be going back after removing '5' which i s the highest number then removing the next highest number/
Your issue is your if statement within your loop.
if (prevnumber == LargestNumber(num).ToString()
{
rearranged.Add(Convert.ToInt32(prevnumber));
//...
}
You only ever add to your List rearranged if the value of prevnumber is the largest value, which is false for every number but 5, so the only value that ever gets added to the list is 5.
That's the answer to why it's only returning 5, but I don't think that will make your method work properly necessarily. You're doing a very dangerous thing by changing the value of the collection you are iterating over (the characters in num) from within the loop itself. Other answers have been written for you containing a method that rearranges the numbers as you've described.
Your Rearrange method is returning List<int> when you try to write that to the console, the best it can do is write System.Collections.Generic.List1[System.Int32] (its type)
Instead of trying to write the list, convert it first into a data type that can be written (string for example)
eg:
var myList = Rearrange("3250");
string concat = String.Join(" ", myList.ToArray());
Console.WriteLine(concat);
Building on pats comment you could iterate through your list and write them to the console.
e.g.
foreach(var i in Rearrange(3250))
{
console.writeline(i.ToString());
}
or if you want to see the linq example.
using system.linq;
Rearrange(3250).foreach(i => console.writeline(i.ToString()));
--edit after seeing you're only getting '5' output
This is because your function only adds number to the list if they are the largest number in your list, which is why 5 is only being added and returned.
Your Rearrange method can be written easily using Array.Sort (or similar with (List<T>) :
int[] Rearrange(int num)
{
var arr = num.ToString ().ToCharArray ();
Array.Sort (arr, (d1, d2) => d2 - d1);
return Array.ConvertAll (arr, ch => ch - '0');
}
Just reading your first sentence
Does not test for integers
static int ReversedNumber(string num)
{
char[] numbers = num.ToCharArray();
Array.Sort(numbers);
Array.Reverse(numbers);
Debug.WriteLine(String.Concat(numbers));
return (int.Parse(String.Concat(numbers)));
}
Because your foreach loop within Rearrange method only loop through the original num. The algorithm doesn't continue to go through the new num string after you have removed the largest number.
You can find the problem by debugging, this foreach loop in Rearrange goes only 4 times if your input string is "3250".

Count Zeroes before the Decimal Point

I am trying to count how many zeroes are a before a decimal.
private void textBox1_TextChanged(object sender, EventArgs e)
{
decimal x = 0;
if (Decimal.TryParse(textBox1.Text, out x))
{
var y = 1000000;
var answer = x * y;
displayLabel2.Text = (x.ToString().Replace(".", "").TrimStart(new Char[] { '0' }) + "00").Substring(0, 2);
}
else
{
displayLabel2.Text = "error";
}
}
When I plug in (lets say) 7.2 I get an output that displays 72, which is what I want. Now I need another display. That initial 7.2 is being multiplied by 1000000. So the quotent of that would be 7,200,000.00. Now I need to some how count the 5 zeroes before the decimal point and display 5 for that. Then if I were to do .72. My Quotent would be 720,000.00. And I would need to display 4, for the 4 zeroes. And so on. Then I need to output that number to displayLabel5.Text
Here's a one line Linq you could try to count zeroes before the decimal. You can Split() first by the decimal then perform a Where().Count() to get the number of zeros.
using System;
using System.Linq;
public class Program
{
public static void Main()
{
string myString = (720000.00).ToString();
Console.WriteLine(myString.Split('.')[0].Where(d => d == '0').Count());
}
}
Results:
4
Demo
Quick and dirty code so be careful, but AFAIK this is the fastest way to do it.
// Input assuming you've sanitised it
string myInputString = "720000.00";
// Remove the decimals
myInputString = myInputString.Substring(0, myInputString.IndexOf("."));
// The count
int count = 0;
// Loop through and count occurrences
foreach (char c in myInputString)
{
if (c == "0")
{
count++;
}
}
Count is now 4.
Guarantee you this is faster than Regex ;-)
Edit: Sorry for the multiple edits, it's been a long day. Need coffee.
use a regular expression to find all the zeros before the period, then get the string length of that match.
Regex regex = new Regex(#"(0+)\.?");
string value1 = "7,200,000.00";
value1 = value1.Replace(",",""); //get rid of the commas
Match match = regex.Match(value1);
if (match.Success)
{
Console.WriteLine(match.Value.Length);
}
As always test the code because I wrote it just now here in this little text box and not in actual visual studio where I could compile and test it myself. But this should at least illustrate the methodology.
Edit:
slight tweak to the regex to account for the possibility that the number will not display a decimal point at all.

Splitting Multiple Numbers

I'm just getting into learning c#, and I'm a little bit over my head. I'm trying to let a user input a list of 5 grades, and then split them to calculate the highest, lowest, and average.
I can't use int and split together, but I'm not sure how to get this to work.
Thanks!
Console.WriteLine("Enter 5 Grades; Whole Integers Only, 0-100; One Space Between Grades");
int allgrades = int.Parse(Console.ReadLine());
string[] splitgrades = allgrades.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
g1.Name = name + "'s GradeBook";
Console.WriteLine(g1.Name);
Console.WriteLine("Press Any Key to Calculate Gradebook Statistics");
Console.ReadKey();
GradeBook book = new GradeBook();
book.AddGrade(splitgrades[0]);
book.AddGrade(splitgrades[1]);
book.AddGrade(splitgrades[2]);
book.AddGrade(splitgrades[3]);
book.AddGrade(splitgrades[4]);
Thank you all for your help; I changed it around to
Console.WriteLine("Enter 5 Grades; Whole Integers Only, 0-100; One Space Between Grades");
string[] splitgrades = Console.ReadLine().Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
int grade = int.Parse(splitgrades[0]);
int grade1 = int.Parse(splitgrades[1]);
int grade2 = int.Parse(splitgrades[2]);
int grade3 = int.Parse(splitgrades[3]);
int grade4 = int.Parse(splitgrades[4]);
In the second line, you're trying to parse the entire input into a single int. You want to first split the string into different ints, then parse each separately.
You're asking the user for a space-delimited list of integers, then trying to parse that string into a single integer. That will cause much sadness during runtime. Instead, you want something like this:
string allGrades = Console.ReadLine();
string[] splitGrades = allGrades.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
This will leave you with an array of (hopefully) 5 integer grades you can add to your GradeBook. When doing so would be the proper time to parse integers from the individual strings in the array.
// Use a foreach instead to allow the user to enter as many grades as they want.
for (int i = 0; i < 5; ++i)
book.AddGrade(int.Parse(splitGrades[i]);

Using C#, how do I read a text file into a matrix of characters and then query that matrix? Is this even possible?

Example
If I had a text file with these lines:
The cat meowed.
The dog barked.
The cat ran up a tree.
I would want to end up with a matrix of rows and columns like this:
0 1 2 3 4 5 6 7 8 9
0| t-h-e- -c-a-t- -m-e-o-w-e-d-.- - - - - - - -
1| t-h-e- -d-o-g- -b-a-r-k-e-d-.- - - - - - - -
2| t-h-e- -c-a-t- -r-a-n- -u-p- -a- -t-r-e-e-.-
Then I would like to query this matrix to quickly determine information about the text file itself. For example, I would quickly be able to tell if everything in column "0" is a "t" (it is).
I realize that this might seem like a strange thing to do. I am trying to ultimately (among other things) determine if various text files are fixed-width delimited without any prior knowledge about the file. I also want to use this matrix to detect patterns.
The actual files that will go through this are quite large.
Thanks!
For example, I would quickly be able to tell if everything in column "0" is a "t" (it is).
int column = 0;
char charToCheck = 't';
bool b = File.ReadLines(filename)
.All(s => (s.Length > column ? s[column] : '\0') == charToCheck);
What you can do is read the first line of your text file and use it as a mask. Compare every next line to the mask and remove every character from the mask that is not the same as the character at the same position. After processing al lines you'll have a list of delimiters.
Btw, code is not very clean but it is a good starter I think.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace DynamicallyDetectFixedWithDelimiter
{
class Program
{
static void Main(string[] args)
{
var sr = new StreamReader(#"C:\Temp\test.txt");
// Get initial list of delimiters
char[] firstLine = sr.ReadLine().ToCharArray();
Dictionary<int, char> delimiters = new Dictionary<int, char>();
for (int i = 0; i < firstLine.Count(); i++)
{
delimiters.Add(i, firstLine[i]);
}
// Read subsequent lines, remove delimeters from
// the dictionary that are not present in subsequent lines
string line;
while ((line = sr.ReadLine()) != null && delimiters.Count() != 0)
{
var subsequentLine = line.ToCharArray();
var invalidDelimiters = new List<int>();
// Compare all chars in first and subsequent line
foreach (var delimiter in delimiters)
{
if (delimiter.Key >= subsequentLine.Count())
{
invalidDelimiters.Add(delimiter.Key);
continue;
}
// Remove delimiter when it differs from the
// character at the same position in a subsequent line
if (subsequentLine[delimiter.Key] != delimiter.Value)
{
invalidDelimiters.Add(delimiter.Key);
}
}
foreach (var invalidDelimiter in invalidDelimiters)
{
delimiters.Remove(invalidDelimiter);
}
}
foreach (var delimiter in delimiters)
{
Console.WriteLine(String.Format("Delimiter at {0} = {1}", delimiter.Key, delimiter.Value));
}
sr.Close();
}
}
}
"I am trying to ultimately (among other things) determine if various text files are fixed-width (...)"
If that's so, you could try this:
public bool isFixedWidth (string fileName)
{
string[] lines = File.ReadAllLines(fileName);
int length = lines[0].Length;
foreach (string s in lines)
{
if (s.length != Length)
{
return false;
}
}
return true;
}
Once you get that lines variable, you can access any character as though they were in a matrix. Like char c = lines[3][1];. However, there is no hard guarantee that all lines are the same length. You could pad them to be the same length as the longest one, if you so wanted.
Also,
"how would I query to get a list of all columns that contain a space character for ALL rows (for example)"
You could try this:
public bool CheckIfAllCharactersInAColumnAreTheSame (string[] lines, int colIndex)
{
char c = lines[0][colIndex];
try
{
foreach (string s in lines)
{
if (s[colIndex] != c)
{
return false;
}
}
return true;
}
catch (IndexOutOfRangeException ex)
{
return false;
}
}
Since it's not clear where you're have difficulty exactly, here are a few pointers.
Reading the file as strings, one per line:
string[] lines = File.ReadAllLines("filename.txt");
Obtaning a jagged array (a matrix) of characters from the lines (this step seems unnecessary since strings can be indexed just like character arrays):
char[][] charMatrix = lines.Select(l => l.ToCharArray()).ToArray();
Example query: whether every character in column 0 is a 't':
bool allTs = charMatrix.All(row => row[0] == 't');

Categories