C# File I/O Efficiency - c#

I have done a homework assignment, here is the problem statement:
Your program should work as follows:
Ask the user to give you a file name. Get the file name and save it.
Open the file.
From the file read a temperature and a wind speed. Both values should be stored in variables declared as double. The file is a text file. Each line of the file contains a temperature and a wind speed value.
Calculate the wind chill factor using a programmer written method, and display the result in the form:
For t = temperature from file
and v = wind speed from file
Wind chill index = calculated result degrees Fahrenheit.
Show all numbers with two digits after the decimal point. (Remember-no magic numbers!)
Repeat these steps until an end of file is encountered.
I have completed the assignment, my code is below, I was just wondering if there was any way to make it more efficient, or if there are some different and creative ways to accomplish this problem, I already turned this in and got 50/50, but I'm just curious as to how some of you advanced and skilled programmers would approach this problem.
using System;
using System.IO;
class Program
{
// declare constants to use in wind chill factor equation - no magic numbers
const double FIRST_EQUATION_NUMBER = 35.74;
const double SECOND_EQUATION_NUMBER = 0.6215;
const double THIRD_EQUATION_NUMBER = 35.75;
const double FOURTH_EQUATION_NUMBER = 0.4275;
const double EQUATION_EXPONENT = 0.16;
const int DEGREE_SYMBOL_NUMBER = 176;
static void Main()
{
// declare and initialize some variables
string filePath = "";
string line = "";
double temperature = 0.0;
double windSpeed = 0.0;
double windChillFactor = 0.0;
char degreeSymbol = (char)DEGREE_SYMBOL_NUMBER;
// ask user for a file path
Console.Write("Please enter a valid file path: ");
filePath = Console.ReadLine();
// create a new instance of the StreamReader class
StreamReader windChillDoc = new StreamReader(#filePath);
// start the read loop
do
{
// read in a line and save it as a string variable
line = windChillDoc.ReadLine();
// is resulting string empty? If not, continue execution
if (line != null)
{
string[] values = line.Split();
temperature = double.Parse(values[0]);
windSpeed = double.Parse(values[1]);
windChillFactor = WindChillCalc(temperature, windSpeed);
Console.WriteLine("\nFor a temperature {0:f2} F{1}", temperature, degreeSymbol);
Console.WriteLine("and a wind velocity {0:f2}mph", windSpeed);
Console.WriteLine("The wind chill factor = {0:f2}{1}\n", windChillFactor, degreeSymbol);
}
} while (line != null);
windChillDoc.Close();
Console.WriteLine("\nReached the end of the file, press enter to exit this program");
Console.ReadLine();
}//End Main()
/// <summary>
/// The WindChillCalc Method
/// Evaluates a wind chill factor at a given temperature and windspeed
/// </summary>
/// <param name="temperature">A given temperature</param>
/// <param name="ws">A given windspeed</param>
/// <returns>The calculated wind chill factor, as a double</returns>
static double WindChillCalc(double temperature, double ws)
{
double wci = 0.0;
wci = FIRST_EQUATION_NUMBER + (SECOND_EQUATION_NUMBER * temperature) - (THIRD_EQUATION_NUMBER * (Math.Pow(ws, EQUATION_EXPONENT))) + (FOURTH_EQUATION_NUMBER * temperature * (Math.Pow(ws, EQUATION_EXPONENT)));
return wci;
}
}//End class Program
Feel free to tell me what you think of it.

Your way looks good, but:
It would look nicer if you used PascalCase for the constants, as that's what coding conventions for c# use.
you should wrap the StreamReader in a using statement, so it gets properly disposed once you're done.
You should probably also wrap it in a try block (and a catch to properly handle the exception) to make sure that you don't get a FileNotFound exception.
It's probably a better idea to structure your while loop the following way:
while((line = windChillDoc.ReadLine()) != null)
{
...
}
[Darn formatting won't work right!]
Other than that though, i wouldn't know as i'm not familiar with weather calculations :)

You're not going to get much zippier than that for file IO in C#. Depending on the size of the data set it may be worth using a buffered reader, but for sufficiently small files, it's just not worth it. I'd leave it as-is.

Most of your comments are extraneous. The code should tell you how...the comments should tell you why.

Minor nitpick, but "WindChillCalc" should be "CalcWindChill" if you are are using English method names (the verb goes first).

string filePath = "";
...
filePath = Console.ReadLine();
Don't initialize with values that are never used; and keep declaration and initialization close together:
string filePath = Console.ReadLine();
using has been mentioned - but don't use # unnecessarily:
new StreamReader(#filePath);
should be just:
new StreamReader(filePath);
Personally, I'd use LINQ for the line-reader, but that is just me ;-p

Why the do/while? In your do you check for null. In your while you check for null. Why not just make it a while statement?
string line;
while((line = windChillDoc.ReadLine()) != null)
{
//Logic
}
EDIT: Fixed the compilation error. Funny thing was I had that originally. This Rich Text Box needs a compiler! :P

Although not really relating to performance (the main question)
IMO:
const double FIRST_EQUATION_NUMBER = 35.74;
const double SECOND_EQUATION_NUMBER = 0.6215;
const double THIRD_EQUATION_NUMBER = 35.75;
const double FOURTH_EQUATION_NUMBER = 0.4275;
const double EQUATION_EXPONENT = 0.16;
isn't much better than a magic number. Looking at that I have no idea what FIRST_EQUATION_NUMBER is used for other than in an equation somewhere, and I can't tell that they are in the same equation or you have four equations which use different numbers? They could also be put into the actual method as thats the only place they are used.
I would change degreeSymbol to a const rather than working it from a const int
later.
const char DEGREE_SYMBOL = (char)176;

If you're getting marked on style etc. then there's a couple extremely minor things
Initializing doubles to 0.0 is redundant.
string.Empty is preferred instead of ""
Your windchill method can be changed to simply (though, during compilation, I think that wci will be optimized out - so it's functionally the same):
(changed formatting for SO readability)
static double WindChillCalc(double temperature, double ws)
{
return FIRST_EQUATION_NUMBER +
(SECOND_EQUATION_NUMBER * temperature) -
(THIRD_EQUATION_NUMBER * (Math.Pow(ws, EQUATION_EXPONENT))) +
(FOURTH_EQUATION_NUMBER * temperature * (Math.Pow(ws, EQUATION_EXPONENT)));
}

In little academic programs like this, unless you do something really dumb, performance will not be an issue. A simple way to tell if performance is an issue is to ask the question: "Does it make me wait?"
If there were an enormous amount of input, I would ask who's providing the input and who's reading the output. That would tell me if I could do the I/O in binary rather than text, because as it is, by far the bulk of the processing will be in the conversion of text into numbers on input, and numbers to text on output, especially floating point.

Related

C# decimals, money change in program and different methods or variables

I'm wondering if someone can help me with decimals? Im going to make a program there can wright price and how much you paid, and then get a change back, but i need to add decimals. The decimals have to round up or down, so if it is 0,11 it goes to 0, if it is o,20 it goes to 0,25. So it will round up to either 0,25, 0,50, 0,75 and later on 1. The change have too also show in which cash you get back, so like 100 bucks, 500 bucks, 50 bucks if you know what i mean. I dont know how to do. This is how long i come:
using System;
namespace inlämninguno
{
class Program
{
// Starting point of my "Change" program
static void Main(string[] args)
{
Console.WriteLine(Subtract()); // adding a subract variable in the beginning of the code
Console.Read();
}
// Using the double datatype to for the decimals
public static double Subtract()
{
Console.WriteLine("Price?"); // how much it costs
string number1Input = Console.ReadLine();
Console.WriteLine("Paid?"); // how much the customer pays
string number2Input = Console.ReadLine();
double prince = Convert.ToDouble(number1Input);
double paid = Convert.ToDouble(number2Input);
// This is for the subtracting part
double num1 = double.Parse(number1Input);
double num2 = double.Parse(number2Input);
double result = num2 - num1; // this is so the console subtracts the values, have to put num2 first so the output isn't negative
Console.Write("Your change is " + result + ":"); // Output "Your change is ..."
return result; // A method that gives the right output of the code
}
}
}
Can someone please help me? Im stuck :D
I tried to convert to double and lots of stuff, but I don't know how to do now.

How does Convert.ToInt32(Console.ReadLine()); work?

I am new to programming in C# and i'm currently doing an exercise that looks like this:
Read the user's first and last name and save this in variables
Welcome the user
Ask the user about their age this year and save this in a variable with the appropriate data type
Calculate previous days the person has lived based on the specified age (age multiplied by 365) and present this.
So my code is working fine, but i would really appreciate if someone could explain the convert thing. I've been making alot of google searches and reading, but i can't seem to understand how it works.
int age = Convert.ToInt32(Console.ReadLine());
Can someone break this down for me for my code?
Thanks!
Console.Title = "Programming in C # - Exercise 1";
// Here the system asks the user to enter their full name.
Console.WriteLine("System: Enter your full name: ");
// Here I declare a variable and assign it a value.
string name = Console.ReadLine();
// Here the system welcomes the user.
Console.WriteLine("System: Welcome " + name + "!");
// Here the system asks how old the user is.
Console.WriteLine("System: How old are you?");
// This is the part i would like to have explained for me.
int age = Convert.ToInt32(Console.ReadLine());
// Here I declare a variable and calculate age in days.
int ageInDays = age * 365;
// Here the system answers.
Console.WriteLine("System: " + age + "?!" + " That means you are " + ageInDays + " days old!");
// Waiting to close the program.
Console.ReadLine();
Although you have a understanding now I'd like to point out that developers/coders should always expect the unexpected e.g. a user enters the wrong type such as for age a value that can not be converted to a integer or the case of name, they simply press enter without entering a name.
So for checking in this case for name being entered use string.IsNullOrWhiteSpace. For integer type, consider using int.TryParse.
In regards to concatenating string values or string values say with integers consider looking at string interpolation, this can be coder preference as some will like to string someString + anotherString or if using strings only there is String.Concat and StringBuilder Class.
Below is a slightly modified version of your code. Since you are just starting out, tuck this away for later or consider learning from this now than later.
using System;
namespace Exercise1
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Programming in C # - Exercise 1";
Console.WriteLine("System: Enter your full name: ");
string fullName = Console.ReadLine();
/*
* Never assume a value for name has been entered
* https://learn.microsoft.com/en-us/dotnet/api/system.string.isnullorwhitespace?view=net-5.0
*/
if (!string.IsNullOrWhiteSpace(fullName))
{
/*
* Note original code concatenated name variable with text, you can also use string interpolation
* https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
*/
Console.WriteLine($"System: Welcome {fullName}!");
}
else
{
Console.WriteLine("System: Welcome no name given!");
}
Console.WriteLine("System: How old are you?");
string ageInput = Console.ReadLine();
/*
* int.TryParse, better than Convert.ToInt32 which is recommended in the remark section
* for documentation for Convert.ToInt32.
*
* https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-5.0
*
*/
if (int.TryParse(ageInput, out var age))
{
int ageInDays = age * 365;
/*
* Note original code concatenated name variable with text, you can also use string interpolation
* https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
*/
Console.WriteLine($"System: {age}?! That means you are {ageInDays} days old!");
}
else
{
Console.WriteLine("No age provided or the value was not a valid integer");
}
Console.WriteLine("Press any key to close this program");
Console.ReadLine();
}
}
}
You read a line from the console and then convert it to a 32bit integer, which is then stored in the variable age.
However this is dangerous, as you do not check if any non numerical characters are given by the user. What does your application do if the user inputs "Foo"? In your application this does not seem like a big problem, but be aware to check for the input first and then continue if the input is valid.
Console.ReadLine reads input from the console in the form of a string data type. This is a data type which, in very very basic terms, represents text. Under the hood, each character is represented by a code. You may have heard some of these encoding terms before, such as ASCII or UTF. For example, A in ASCII is represented by the number 65. This is also true for numbers. The ASCII code 48 represents the number 0.
We sometimes need to convert the string representation of a number to its actual numerical value. Often for performing mathematical operations and such. To do that, we need to get that "0 which is represented as 48 in ASCII" to a literal 0. This is done by Convert.ToInt32 as you're doing. It reads the string, and says "Oh i'm the text representation of a 0, therefore I will output the number 0".
So in a very brief summary, its taking text representation of a number and changes the data to a literal numerical value.
Console.Realine() in C# gives you a output of string..
So in order to convert to numerical... Convert Function will be useful..

Unity formatting multiple numbers

So I'm a complete newb to unity and c# and I'm trying to make my first mobile incremental game. I know how to format a variable from (e.g.) 1000 >>> 1k however I have several variables that can go up to decillion+ so I imagine having to check every variable's value seperately up to decillion+ will be quite inefficient. Being a newb I'm not sure how to go about it, maybe a for loop or something?
EDIT: I'm checking if x is greater than a certain value. For example if it's greater than 1,000, display 1k. If it's greater than 1,000,000, display 1m...etc etc
This is my current code for checking if x is greater than 1000 however I don't think copy pasting this against other values would be very efficient;
if (totalCash > 1000)
{
totalCashk = totalCash / 1000;
totalCashTxt.text = "$" + totalCashk.ToString("F1") + "k";
}
So, I agree that copying code is not efficient. That's why people invented functions!
How about simply wrapping your formatting into function, eg. named prettyCurrency?
So you can simply write:
totalCashTxt.text = prettyCurrency(totalCashk);
Also, instead of writing ton of ifs you can handle this case with logarithm with base of 10 to determine number of digits. Example in pure C# below:
using System.IO;
using System;
class Program
{
// Very simple example, gonna throw exception for numbers bigger than 10^12
static readonly string[] suffixes = {"", "k", "M", "G"};
static string prettyCurrency(long cash, string prefix="$")
{
int k;
if(cash == 0)
k = 0; // log10 of 0 is not valid
else
k = (int)(Math.Log10(cash) / 3); // get number of digits and divide by 3
var dividor = Math.Pow(10,k*3); // actual number we print
var text = prefix + (cash/dividor).ToString("F1") + suffixes[k];
return text;
}
static void Main()
{
Console.WriteLine(prettyCurrency(0));
Console.WriteLine(prettyCurrency(333));
Console.WriteLine(prettyCurrency(3145));
Console.WriteLine(prettyCurrency(314512455));
Console.WriteLine(prettyCurrency(31451242545));
}
}
OUTPUT:
$0.0
$333.0
$3.1k
$314.5M
$31.5G
Also, you might think about introducing a new type, which implements this function as its ToString() overload.
EDIT:
I forgot about 0 in input, now it is fixed. And indeed, as #Draco18s said in his comment nor int nor long will handle really big numbers, so you can either use external library like BigInteger or switch to double which will lose his precision when numbers becomes bigger and bigger. (e.g. 1000000000000000.0 + 1 might be equal to 1000000000000000.0). If you choose the latter you should change my function to handle numbers in range (0.0,1.0), for which log10 is negative.

Using TextFieldParser.FixedWidth in variable length strings

I am working on a parser that is intended to read in data in fixed-width format (8 char x 10 col). However, sometimes this isn't the case, and there is sometimes valid data in the areas that do not meet this. It is not safe to assume that there is an escape character (such as the + in the figure below), as that is one of several formats.
I had attempted using TextFieldParser.FixedWidth, and giving it a 8x10 input, but anything that does not meet this quantity is sent to the ErrorLine instead.
It doesn't seem like it would be good practice to parse from my exception catching block, is it?
Since it is only discrepant lines who require additional work, is a brute force submethod the best approach? All of my data always comes in 8 char blocks. the final block in a line can be tricky in that it may be shorter if it was manually entered. (Predicated on #1 being OK to do)
Is there a better tool to be using? I feel like I'm trying to fit a square peg in a round hole with a fixedwidth textfieldparser.
Note: Delimited parsing is not an option, see the 2nd figure.
edit for clarification: the text below is a pair of excerpts of input decks for NASTRAN, a finite element code. I am aiming to have a generalized parsing method that will read the text files in, and then hand off the split up string[]s to other methods to actually process each card into a specific mapped object. (e.g. in the image below, the two object types are RBE3 and SET1)
Extracted Method:
public static IEnumerable<string[]> ParseFixed(string fileName, int width, int colCount)
{
var fieldArrayList = new List<string[]>();
using (var tfp = new TextFieldParser(fileName))
{
tfp.TextFieldType = FieldType.FixedWidth;
var fieldWidths = new int[colCount];
for (int i = 0; i < fieldWidths.Length; i++)
{
fieldWidths[i] = width;
}
tfp.CommentTokens = new string[] { "$" };
tfp.FieldWidths = fieldWidths;
tfp.TrimWhiteSpace = true;
while (!tfp.EndOfData)
{
try
{
fieldArrayList.Add(tfp.ReadFields());
}
catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
{
Debug.WriteLine(ex.ToString());
// parse atypical lines here...?
continue;
}
}
}
return fieldArrayList;
}

How to translate shutter speed values into usable data?

I'm gathering light statistics of EXIF data for a large collection of photos and I'm trying to find the simplest way (i.e. performance doesn't matter) of translating Exposure Time values to/from usable data. There is (as far as I can find) no standard for what values camera manufacturers might use i.e. I can't just scan the web for random images and hard-code a map.
Here are is a sample set of values I've encountered (" indicates seconds):
279", 30", 5", 3.2", 1.6", 1.3", 1", 1/1.3, 1/1.6, 1/2, 1/2.5, 1/3, 1/4, 1/5, 1/8, 1/13, 1/8000, 1/16000
Also take into consideration that I'd also like to find the average (mean) ... but it should be one of the actual shutter speeds collected and not just some arbitrary number.
EDIT:
By usable data I mean some sort of creative? numbering system that I can convert to/from for performing calculations. I thought about just multiplying everything by 1,000,000 except some fractions when divided are quite exotic.
EDIT #2:
To clarify, I'm using ExposureTime instead of ShutterSpeed because it contains photographer friendly values e.g. 1/50. ShutterSpeed is more of an approximation (which varies between camera manufacturers) and leads to values such as 1/49.
You want to parse them into some kind of time-duration object.
A simple way, looking at that data, would be to check wheter " or / occurs, if " parse as seconds, / parse as fraction of seconds. I don't really understand what else you could mean. For an example you'd need to specify a language--also, there might be a parser out there already.
Shutter speed is encoded in the EXIF metadata as an SRATIONAL, 32-bits for the numerator and 32-bits for the denominator. Sample code that retrieves it, using System.Drawing:
var bmp = new Bitmap(#"c:\temp\canon-ixus.jpg");
if (bmp.PropertyIdList.Contains(37377)) {
byte[] spencoded = bmp.GetPropertyItem(37377).Value;
int numerator = BitConverter.ToInt32(spencoded, 0);
int denominator = BitConverter.ToInt32(spencoded, 4);
Console.WriteLine("Shutter speed = {0}/{1}", numerator, denominator);
}
Output: Shutter speed = 553859/65536, sample image retrieved here.
It seems there are three types of string you will encounter:
String with double quotes " for seconds
String with leading 1/
String with no special characters
I propose you simply test for these conditions and parse the value accordingly using floats:
string[] InputSpeeds = new[] { "279\"", "30\"", "5\"", "3.2\"", "1.6\"", "1.3\"", "1\"", "1/1.3", "1/1.6", "1/2", "1/2.5", "1/3", "1/4", "1/5", "1/8", "1/13", "1/8000", "1/16000" };
List<float> OutputSpeeds = new List<float>();
foreach (string s in InputSpeeds)
{
float ConvertedSpeed;
if (s.Contains("\""))
{
float.TryParse(s.Replace("\"", String.Empty), out ConvertedSpeed);
OutputSpeeds.Add(ConvertedSpeed);
}
else if (s.Contains("1/"))
{
float.TryParse(s.Remove(0, 2), out ConvertedSpeed);
if (ConvertedSpeed == 0)
{
OutputSpeeds.Add(0F);
}
else
{
OutputSpeeds.Add(1 / ConvertedSpeed);
}
}
else
{
float.TryParse(s, out ConvertedSpeed);
OutputSpeeds.Add(ConvertedSpeed);
}
}
If you want TimeSpans simply change the List<float> to List<TimeSpan> and instead of adding the float to the list, use TimeSpan.FromSeconds(ConvertedSpeed);

Categories