I have a problem my code is not efficient enough. He thinks he knows the content. How do I write the code so it can work with any file. So he practically only excludes numbers and ignores the words (strings).
public static int SumUpFileContent(string file)
{
int sum = 0;
var lines = File.ReadAllLines(file);
foreach (var line in lines)
{
if (int.TryParse(line, out int i))
sum += i;
}
return sum;
}
Keep in mind :
This doesn't work with numbers that have decimals, only integers. replace int.TryParse() with double.TryParse() if you have to.
The data must come in a very specific format (i.e. every entry must be on its own line)
From the information you provided you could split the file content in to an array
then for each item in the array use an int.tryParse to see if it is a number. (this is assumed that the numbers are always int)
Related
We need to process a lot of strings, which contain many numbers (formatted as strings) and are separated by an arbitrary character.
Ex: numbers separated by a space:
var s = "1234.555 43434.43 434.436 85656.253 564.5656 <etc.>"
Now we are using String.Split to first create an array of strings (containing each number) and then parse these using double.Parse() to a numeric type.
var stringArr = s.Split(separator, StringSplitOptions.RemoveEmptyEntries);
// Some iteration/loop in which at some point this routine is called
var d = double.Parse(stringArr[i]);
But we notice this takes up a lot of resources (memory allocation and CPU), so we would like to create at least a less memory-consuming solution.
The first step is to enumerate the number occurrences in the string and then parse the substrings to a number.
For enumerating, we already found a nice solution on SO:
What are the alternatives to Split a string in c# that don't use String.Split()
However, this routine then uses Substring to read a parting string from the entire string, so I would expect this results (again) in an extra allocation of memory to be able to use/read that partial string...
If so (regarding memory allocation), is there a way to only parse the found part directly from the original string (without allocating a new string) to a numeric value...
I don't mind using unsafe code, if necessary... It will only be marked unsafe during the brief period the numbers are extracted.
separated by an arbitrary character
this forces you to either only handle integers, or to enforce some limitations on what characters can be used as separators and/or decimal separator. For example
1 234.567
might be interpreted as either a single number, two number, or three numbers depending on what characters you want to allow as separators. Note that the thousand separator and decimal separators depend on culture just to make things more complicated.
If you want to minimize memory usage, Double.Parse has a overload that takes a ReadOnlySpan<char> s as input. So just iterate over your string, once you encounter a number character you save the index and iterate to the next non-number character, and use Span<T>.Slice to create a span over the number digits and parse these. Continue until you have parsed all characters.
But you will still need to define what characters counts as part of a number or not. Note that you might want to Parse with the CultureInfo.InvariantCulture to get consistent behavior regardless of region.
I fiddled something.
Based on the assumption, that the "arbitrary" split char is known.
I also added the variant as entrypoint to a DataFlowPipeline, because ReadOnlySpan has some quirks with async and enumeration :D
It is a naive approach to give an idea. You may want to add some sanity checks etc.
using System;
using System.Collections.Generic;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
var s = "1234.555 43434.43 434.436 85656.253 564.5656";
//foreach( var d in Parse(s, ' ') ) Console.WriteLine(d);
var ab = new ActionBlock<double>(d => Console.WriteLine(d));
ParseAndEnqueue(s, ' ', ab);
await ab.Completion;
}
public static IEnumerable<double> Parse(ReadOnlySpan<char> input, char splitChar)
{
var result = new List<double>();
int nextIndex = input.IndexOf(splitChar);
while( nextIndex >= 0 )
{
if(double.TryParse(input[..nextIndex], out double myVal)) result.Add(myVal);
if( nextIndex+1 >= input.Length ) break;
input = input[(nextIndex+1)..];
nextIndex = input.IndexOf(splitChar);
if( nextIndex < 0 )
if(double.TryParse(input, out double myLastVal)) result.Add(myLastVal);
}
return result;
}
public static void ParseAndEnqueue(ReadOnlySpan<char> input, char splitChar, ActionBlock<double> ab)
{
int nextIndex = input.IndexOf(splitChar);
while( nextIndex >= 0 )
{
if(double.TryParse(input[..nextIndex], out double myVal)) ab.Post(myVal);
if( nextIndex+1 >= input.Length ) break;
input = input[(nextIndex+1)..];
nextIndex = input.IndexOf(splitChar);
if( nextIndex < 0 )
if(double.TryParse(input, out double myLastVal)) ab.Post(myLastVal);
}
ab.Complete();
}
}
=> Fiddle
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".
Here is what I have so far, obviously you can subtract arrays the way i did. And I also need to know how to write the new list to a .txt file that i already have ("records.txt")
public static int deleteRecord(string num)
{
int amount;
int.TryParse(num, out amount);
string[] arrayRecords = File.ReadAllLines("Records.txt").ToArray();
string[] newArrayRecords = arrayRecords - arrayRecords[amount];
for (int i = 0; i < amount; i++)
{
Console.WriteLine(newArrayRecords[amount]);
}
Console.WriteLine(amount);
return amount;
}
I assume that you want to delete a particular value from a file and that is why you have chosen the "num" parameter to be a string.
If so then this will work:
public static void deleteRecord(string num)
{
var lines = File.ReadAllLines("Records.txt").ToList();
if (lines.Remove(num) == true)
{
File.WriteAllLines("Records.txt", lines.ToArray<string>());
}
}
There are a couple of things to point out in your code. Firstly in your example, if you couldn't convert num to an int then you would be trying to remove the value of 0 from your file - which you may not want.
Secondly File.ReadAllLines already returns an Array of strings, so you don't need the .ToArray() at the end. In fact that converts the string[] array to an object[] array - which is not what you want.
I've converted it to a List as they are easier to work with. I only save the file if the item has been removed.
Hope that helps...
I presume that you want to remove the line that contains specified amount, if so you can try this:
var lines = File.ReadLines("Records.txt")
.Where(x => !x.Contains(amount.ToString());
// this will replace all prev. lines with the new ones
File.WriteAllLines("Records.txt", lines);
If you want to remove all lines that comes before this line then you can try:
var allLines = File.ReadLines("Records.txt");
var line = allLines.Where(x => x.Contains(amount.ToString()).First();
var lineIndex = allLines.IndexOf(line);
File.WriteAllLines("Records.txt",lines.GetRange(lineIndex, allLines.Count - lineIndex));
Ofcourse that answer assumes that there is line that contains amount.If there isn't then second code snippet could possibly throw exception.
Let's say I know how many numbers will user put in. I have an int array and I want to fill it with integers user put in devided by particular character, for example space. I managed to solve it this way.
int[] numbers = new int[5];
string[] input = Console.ReadLine().Split(' ');
for (int i = 0; i < numbers.Length; ++i)
numbers[i] = int.Parse(input[i]);
I want to ask you, is there any other, better way to do this?
You could use Linq:
var numbers = Console.ReadLine().Split(' ').Select(token => int.Parse(token));
// if you must have it as an array...
int[] arr = numbers.ToArray();
This basically does the same thing as your code, it's just more concise.
You could also make your code more robust by handling cases where the user inputs something that's not a number (which would cause int.Parse() to throw an exception).
Suggestion 1: if you would like to read all values from single line delimited by space, then i would suggest you to use command line arguments. so that you can atleast avoid using String array in your program as command line arguments are stored into the Main() method String array argument.
Suggestion 2: while parsing the values into Int it would be nice to check wether parsing is successfull or not, so that if the parsing failes(due to invalid integer) then we can store zero or some default value.
Int32.TryParse() allows you to let you know wether parsing is sucessfull or not.
Try This:
static void Main(string[] args)
{
int[] numbers = new int[args.Length];
int temp;
for(int i=0;i<args.Length;i++)
{
if (Int32.TryParse(args[i],out temp))
{
numbers[i] = temp;
}
else
{
numbers[i] = 0;
}
}
}
Let me try, just read console line and split by space. use ConvertAll method fro Array class.
int[] myArray = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
Even though the return value from Console.Read() returns an integer, it isn't quite that simple. It will return the integer representation of the characters you type in.
In order to retrieve the number of elements, as your code attempts to do. You should refactor a small portion into this:
Console.WriteLine("enter no of elements");
c = int.Parse(Console.ReadLine());
Please note that this piece of code can throw exceptions if what you type in isn't a number
You can use StringTokenizer to tokenize the whole line and then use parseInt().
I'm reading in a text file using BinaryReader, then doing what I want with it (stripping characters, etc..), then writing it out using BinaryWriter.
Nice and simple.
One of the things I need to do before I strip anything is to:
Check that the amount of characters in the file is even (obviously file.Length % 2) and
If the length is even, check that every preceding character is a zero.
For example:
0, 10, 0, 20, 0, 30, 0, 40.
I need to verify that every second character is a zero.
Any ideas? Some sort of clever for loop?
OKAY!
I need to be a lot more clear about what I'm doing. I have file.txt that contains 'records'. Let's just say it's a comma delimited file. Now, What my program needs to do is read through this file, byte by byte, and strip all of the characters we don't want. I have done that. But, some of the files that will be going through this program will be single byte, and some will be double byte. I need to deal with both of these possibilities. But, I need to figure out whether the file is single or double byte in the first place.
Now, obviously if the file is double byte:
The file length will be divisible by 2 and
Every preceding character will be a zero.
and THAT'S why I need to do this.
I hope this clears some stuff up..
UPDATE!
I'm just going to have a boolean in the arguments - is16Bit. Thanks for your help guys! I would have rather deleted the question but it won't let me..
Something like this in a static class:
public static IEnumerable<T> EveryOther(this IEnumerable<T> list)
{
bool send = true;
foreach(var item in list)
{
if (send) yield return item;
send = !send;
}
}
and then (using the namespace of the previous class)
bool everyOtherIsZero = theBytes.EveryOther().All(c => c == 0);
string[] foo = file.text.Split(new{','}, StringSplitOptions.RemoveEmptyEntries);
for(int i=0; i<foo .Length; i+=2)
{
if(file[i]!="0")
return false;
}
How about this
string content = File.ReadAllText(#"c:\test.txt");
if (content.Length % 2 != 0)
throw new Exception("not even");
for(int i = 0; i < content.Length; i+=2)
if (content[i] != '0')
throw new Exception("no zero found");