c#. array keeps resetting itself in my switch - c#

I'm currently in programming 101 and I've been stuck for a long time with an issue.
My assignment wants me to create a class with a bunch of methods to run with a switch case( as a menu).
If I press 1 (add passenger), it does what it's supposed to do the first time, but the second time, it just restarts the array.
I can't seem to save my answer to the array and then move on to the next spot in the array.
Would someone please explain how I'm supposed to "call" the array and save in it outside my for-loop in method add_pass?
using System;
namespace bussen
{
class buss
{
public int[] passagerare;
public int numbof_passagerare;
public void Run()
{
int nmr = 0;
do
{
Console.WriteLine("options:");
Console.WriteLine("1 add passenger");
Console.WriteLine("2 print out all passengers");
Console.WriteLine("3 calc average age");
Console.WriteLine("0 close program");
nmr = int.Parse(Console.ReadLine());
switch (nmr)
{
case 1:add_pass();
break;
case 2:all_pass();
break;
case 3:
break;
case 0:
break;
}
} while (nmr != 0);
}
public void add_pass()
{
if (passagerare.Length < 5)
{
for (int i = 0; i < passagerare.Length; i++)
{
Console.WriteLine("type age of passenger");
int numbof_passenger = int.Parse(Console.ReadLine());
passagerare[i] = numbof_passenger;
break;
}
}
else if(passagerare.Length >= 5)
{
Console.WriteLine("buss is full");
}
}
public void all_pass()
{
foreach(int index in passagerare)
{
Console.WriteLine(index);
}
}
}
class Program
{
static void Main(string[] args)
{
var minbuss = new buss();
minbuss.Run();
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}

It's starting over because you're telling it to start over. The for loop starts at 0 every time.
for (int i = 0; i < passagerare.Length; i++)
{
Console.WriteLine("type age of passenger");
int numbof_passenger = int.Parse(Console.ReadLine());
passagerare[i] = numbof_passenger;
break;
}
You need to find the length of the array and append the new entry. Try using a List instead of any array.

The issue was within the add_pass method, where i used a for - loop to add passengers. each time i pressed number 1 in the menu to call the method , the loop restarted the array wich i was saving the integers to.To get this right i just called the method without a loop like shown below. Thanks everybody for your help!
public void add_pass()
{
if (numbof_passagerare < 5)
{
Console.WriteLine("how old is the passenger");
int age = int.Parse(Console.ReadLine());
passagerare[numbof_passagerare++] = age;
}
else if(numbof_passagerare == 5)
{
Console.WriteLine("buss is full!");
}

Related

C# Fast Food Menu

I'm writing a program that displays a fast food menu and it allows the user to select an item. Then, the user enters the quantity of that item, and can continue selecting items with specific quantities until done. What I'm having trouble with is finding out how to calculate a running total. I am new to c# so I only know the basics. What is the best way to keep track of the running total while using multiple methods? In addition, I'm open to any criticism in my code. Thank you in advance.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
bool ordering = true;
string userInput;
double itemPrice;
double itemQuantity;
double subTol;
string response;
void pricing()
{
Console.Write("Enter option 1, 2, 3: ");
userInput = Console.ReadLine();
switch (userInput)
{
case "1":
itemPrice = 3.00;
Console.WriteLine("You have picked a burger.");
break;
case "2":
itemPrice = 1.50;
Console.WriteLine("You have picked fries.");
break;
case "3":
itemPrice = 1.00;
Console.WriteLine("You have picked a soda.");
break;
default:
Console.WriteLine("That is not on our menu.");
pricing();
break;
}
}
void quantity()
{
Console.Write("Enter quantity: ");
itemQuantity = Convert.ToDouble(Console.ReadLine());
}
void subTotal()
{
subTol = itemQuantity * itemPrice;
Console.WriteLine();
Console.WriteLine("Your Subtotal: " + subTol);
}
while (ordering)
{
Console.WriteLine("What would you like from our menu?");
Console.WriteLine("\n1. Burger ($3.00) \n2. Fries ($1.50) \n3. Soda ($1.00)");
Console.WriteLine();
pricing();
quantity();
subTotal();
Console.Write("Would you like anything else? Y/N: ");
response = Console.ReadLine();
response = response.ToUpper();
if (response == "Y")
{
ordering = true;
}
else
{
ordering = false;
Console.WriteLine("Enjoy your meal!");
}
}
}
}
}
First of all, for the
If(response==“Y”)
Part you can actually just say
ordering = response == “Y”;
Also, this would only accept uppercase y. String.EqualsIgnoreCase() or something similar would probably do a better job
ordering = response.equalsIgnoreCase(“Y”);
You could keep a list of how many of each item the person has ordered, like
Int burgers = 0; and so on.
Then for the total, you could get 3*burgers + 1.5*fries and so on. However, if you want custom orders, using a struct might be better. For example, a struct could contain a type(burger or fries) and a price, depending on how many additions they have. You could also just add the price of each item to a total every time you order. This seems like a fun project, and I hope I could help!
For keeping track of total, the best approach would be to have a list of ordered items with their quantities, where you go and put each order from the user.
When you want to calculate the subtotal or total, you just go and compute from the list.
Here are some insights on how you can accomplish that, with some changes to the code you shared. Check the comments for details and reasoning.
namespace ConsoleApp1{
// creating record classes to store structured data
record MenuItem(string Product, decimal Price);
record Order(MenuItem Item, int Quantity);
class Program {
static void Main(string[] args) {
// better to keep your menu options & prices in a central place,
// instead of hardcoding them everywhere
List<MenuItem> menu = new() {
new ("Burger", 3.00M),
new ("Fries", 1.50M),
new ("Soda", 1.00M),
};
// this is a list to store the ordered items so far
List<Order> orders = new();
// moved most of the procedures to separate functions
do {
PrintMenu(menu);
var item = InputItem(menu);
var quantity = InputQuantity();
orders.Add(new Order(item, quantity));
PrintSubTotal(orders);
} while (CheckIfContinue());
PrintTotal(orders);
ByeBye();
}
static void PrintMenu(List<MenuItem> menu) {
// print each menu entry in a loop
Console.WriteLine("What would you like from our menu?");
for (int i = 0; i < menu.Count; i++) {
// adding 1 to the index for printing, because in c# indexes start at 0
Console.WriteLine($"{i + 1}: {menu[i].Product} (${menu[i].Price})");
}
Console.WriteLine();
}
static MenuItem InputItem(List<MenuItem> menu) {
// enter a loop, will be broken only if the input is correct
while (true) {
Console.Write($"Enter option: 1 to {menu.Count}: ");
if (int.TryParse(Console.ReadLine(), out int i) && i > 0 && i <= menu.Count) {
// must subtract 1 from the index, because indexes start from 0 in c#
Console.WriteLine($"You have picked {i}: {menu[i - 1].Product}.\n");
// if the input is a valid int
return menu[i - 1];
}
Console.WriteLine("That is not in our menu.\n");
}
}
static int InputQuantity() {
// same thing here, will repeat the loop unless the input is valid
while (true) {
Console.Write("Enter quantity: ");
if (int.TryParse(Console.ReadLine(), out int i) && i > 0) {
return i;
}
Console.WriteLine("Invalid quantity.\n");
}
}
static void PrintSubTotal(List<Order> orders) {
// using LINQ to iterate the list of orders sum up the prices x quantities
Console.WriteLine($"Your Subtotal: ${orders.Sum(o => o.Item.Price * o.Quantity)}\n");
}
static bool CheckIfContinue() {
Console.Write("Would you like anything else? Y/N: ");
// will return True if the input is y
return Console.ReadLine()?.Trim().ToUpper() == "Y";
}
static void PrintTotal(List<Order> orders) {
Console.WriteLine($"Your Order:");
foreach (var order in orders) {
// subtotal for each order item
Console.WriteLine($"{order.Item.Product, -10} x{order.Quantity, -4}: ${order.Item.Price * order.Quantity}");
}
// total for the whole order
Console.WriteLine($"---------- Total: ${orders.Sum(o => o.Item.Price * o.Quantity)}\n");
}
static void ByeBye() {
Console.WriteLine("Enjoy your meal!");
}
}
}

solution needed with a homework c# (cs7036)

I am trying to make a menu with different options on replit where the user can choose an option.
I am making the options on seperate files, so I later can call for the methods from those files instead of writing everything in the main file.
One of these menuoptions will have an option with a random generator which will generate a random string.
I created the method on another file, But when it gave me an error (7036) when i tried to call it to the main method.
I have pasted an example of the code here below, But you can also access and run the code on this link: https://replit.com/#AY2002/testc22#main.cs Which will be easier to understand. I am a begginer and, Therefore seeking a simple answer.
Thank you!
//MAIN replit FILE
using System;
using System.Collections.Generic;
namespace Namespace1 {
// This is the main file where the menu is built. the menu is working fine.
// the menu have 4 options and an exit option, which will be divided into 4 different replit files and one of them will have a method that randomly generates a string. you can see the method when you scroll down near to the bottom of the main file.
class Program {
public static void Main (string[] args)
{
string[] Menuchoises = new string [] {"Choise1","Choise2","Choise3","Choise4","Choise5"};
int x = 0;
while (true){
Console.Clear();
Console.WriteLine("welcome to menu");
Console.WriteLine();
Console.CursorVisible = false;
if(x == 0) {
Console.WriteLine(" " + Menuchoises[0] + " {");
Console.WriteLine(Menuchoises[1]);
Console.WriteLine(Menuchoises[2]);
Console.WriteLine(Menuchoises[3]);
Console.WriteLine(Menuchoises[4]);
}
else if(x == 1) {
Console.WriteLine(Menuchoises[0]);
Console.WriteLine(" " + Menuchoises[1] + " {");
Console.WriteLine(Menuchoises[2]);
Console.WriteLine(Menuchoises[3]);
Console.WriteLine(Menuchoises[4]);
}
else if(x == 2) {
Console.WriteLine(Menuchoises[0]);
Console.WriteLine(Menuchoises[1]);
Console.WriteLine(" " + Menuchoises[2] + " {");
Console.WriteLine(Menuchoises[3]);
Console.WriteLine(Menuchoises[4]);
}
else if(x == 3) {
Console.WriteLine(Menuchoises[0]);
Console.WriteLine(Menuchoises[1]);
Console.WriteLine(Menuchoises[2]);
Console.WriteLine(" " + Menuchoises[3] + " {");
Console.WriteLine(Menuchoises[4]);
}
else if(x == 4) {
Console.WriteLine(Menuchoises[0]);
Console.WriteLine(Menuchoises[1]);
Console.WriteLine(Menuchoises[2]);
Console.WriteLine(Menuchoises[3]);
Console.WriteLine("\t" + Menuchoises[4] + " {");
}
var key = Console.ReadKey();
if (key.Key == ConsoleKey.DownArrow && x != Menuchoises.Length -1) {
x++;
}
else if (key.Key == ConsoleKey.UpArrow && x>=1) {
x--;
} else if (key.Key == ConsoleKey.Enter) {
switch (x) {
case 0:
Menuchoise1();
break;
case 1:
Menuchoise2();
break;
case 2:
Menuchoise3();
break;
case 3:
Menuchoise4();
break;
case 4:
Menuchoise5();
break;
}
}
}
}
public static void Menuchoise1() {
// Class2.second is the name of the second class which will be the method that will appear when you choose the 1st option in the menu.
// The second class is in the second file which you`ll see below the main file
// The CS 7036 error seems to be appearing here
Class2.second();
Console.Clear();
Console.ReadKey();
}
public static void Menuchoise2() {
Console.Clear();
Console.ReadKey();
}
public static void Menuchoise3() {
Console.Clear();
Console.ReadKey();
}
public static void Menuchoise4() {
Console.Clear();
Console.ReadKey();
}
public static void Menuchoise5() {
Console.Clear();
Console.WriteLine("press enter to exit the menu");
Console.ReadKey();
Console.Clear();
Environment.Exit(1);
}
}
}
// SECOND replit FILE
//this is the second file where i have the random value generator.
using System;
using System.Collections.Generic;
namespace Namespace1 {
// class name of the second file
public class Class2 {
// the string[]args function that will later be put in the main file in order to use this method in the menu
public static string second(string[] Random) {
string[] RandomChoises = new string [4];
// list on options which will be randomly generated
RandomChoises[0] = "C1";
RandomChoises[1] = "C2";
RandomChoises[2] = "C3";
RandomChoises[3] = "C4";
RandomChoises[4] = "C5";
for (int i = 0; i < RandomChoises.Length; i++)
{
Console.WriteLine(RandomChoises[i]);
}
// the choises are randomly generated here
Random rnd = new Random();
int Randomanswer = rnd.Next(1,RandomChoises.Length);
Console.WriteLine("You got the answer: " + RandomChoises[Randomanswer]);
return Convert.ToString(Randomanswer);
}
}
}
As mentioned in the comments, the problem is that the second method takes a parameter, but is not called with one. Since the parameter is not used it should be removed. Cleaning up the class a bit should give you something like:
public static class RandomHelpers{
public static string GetRandomValue() {
// use collection initializer
string[] choices= new []{"C1","C2","C3","C4","C5"}
// use foreach loop
foreach(var choice in choices){
Console.WriteLine(choice );
}
// You should probably not recreate the random object for each method call
// But this should work fine for demonstration purposes
Random rnd = new Random();
// start from zero
int randomIndex = rnd.Next(0,RandomChoises.Length);
var randomValue = choices[randomIndex];
// You should probably just return the random value,
// and let the caller print it to console.
Console.WriteLine("You got the answer: " + randomValue );
// Either return the index as an int, or the value as a string
// Do not convert numbers to strings unless you are writing to the console/file
return randomValue ;
}
}
This should work better. As you get more experience you should find better ways to split functionality into reusable methods.
You have an issue with the way you are defining the array in second.cs. You either need explicitly say how many elements your array will have, like this:
string[] RandomChoice = new string[5];
Or you can leave the number out and let the compiler infer it from the number of elements you put in the {}, like this:
string[] RandomChoises = new string[] { "C1", "C2", "C3", "C4", "C5" };
You can read more details about declaring arrays in C# here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/
After you fix that see JonasH answer about the method parameters in second().

c# input only using slot 0

I'm trying to get a user to input 1 value into the array, and then exit to main menu, then the next person to enter a value gets his put into the next bracket, not replace slot 0, currently it just loops the first array bracket and replaces the number. Help would be much appreciated
static void Main(string[] args)
{
int[] myArray = new int[10];
while (true)
{
int enteredNumber;
Startmenu();
enteredNumber = Convert.ToInt32(Console.ReadLine());
if (enteredNumber == 1)
{
for (int i = 0; i < myArray.Length; i++)
{
Console.WriteLine("Insert Number:");
myArray[i] = Convert.ToInt32(Console.ReadLine());
}
Console.Clear();
Console.WriteLine("blabla");
Thread.Sleep(2000);
Console.Clear();
}
if (enteredNumber == 9)
{
if (Login(1234, 3) == true)
{
foreach (int number in myArray)
{
Console.WriteLine(number);
}
}
}
}
}
One problem I think you have in that code is that you're trying to do too much in one method. You might want to break out discreet operations into separate methods, which will both enable code reuse and also serve to make your code more readable.
First, you need a method that will get an int from the user. Let's write a helper method that does that, which takes in a string that will be displayed as a prompt to the user, and which will continue to prompt them until they enter a valid int. We can use int.TryParse to determine if they enter a valie int. It takes a string argument to parse, and an out int that will contain the parsed value if it succeeds. The method itself returns true if it was successful:
private static int GetIntFromUser(string prompt)
{
int result;
do
{
Console.Write(prompt);
} while (!int.TryParse(Console.ReadLine(), out result));
return result;
}
You don't show the code for ShowMenu, but it probably gives the user some menu choices, and I've added the functionality where it returns the menu choice entered by the user:
private static int ShowMenu()
{
int menuChoice;
do
{
Console.Clear();
Console.WriteLine("Main Menu:");
Console.WriteLine("1. Input an array item");
Console.WriteLine("9. Print the numbers\n");
menuChoice = GetIntFromUser("Enter your choice (1 or 9): ");
} while (menuChoice != 1 && menuChoice != 9);
return menuChoice;
}
Now, another thing you seem to be missing that might help here, is to add a "pause" to the program after printing out the array items, so the user has a chance to see them. I think this is probably the main problem. Currently you write them all out to the screen, then the while loop runs again and you don't get a chance to see it. Here's a helper method that asks the user to press a key when they're ready, and then waits for the user to press a key:
private static void WaitForUserInput()
{
Console.Write("\nPress any key to continue...");
Console.ReadKey();
}
We can also put the code to populate the array in a separate method. Since you want to enable the user to populate only one item at a time, we will need to keep track of which item is the next one to populate, so we'll set a class-level variable for that, and make use of it in the method. We'll populate that item, then increment the variable. Of course we have to first make sure the next item is not greater than the number of items available:
private static int NextIndexToPopulate = 0;
private static void PopulateNextArrayItem(int[] array)
{
if (array == null) return;
if (NextIndexToPopulate >= array.Length)
{
Console.WriteLine("The array is full; no items left to enter.");
WaitForUserInput();
}
else
{
array[NextIndexToPopulate] = GetIntFromUser("Enter number to insert: ");
NextIndexToPopulate++;
}
}
And another method can be used to print out the array:
private static void PrintArray(int[] array)
{
if (array == null)
{
Console.WriteLine("The array is null - nothing to print.");
}
else
{
Console.WriteLine("Here are the array items entered:");
for (int i = 0; i < NextIndexToPopulate; i++)
{
Console.WriteLine(array[i]);
}
}
}
You also didn't show the Login code, so here's a dummy method I'll use that always returns true:
private static bool Login(int first, int second)
{
return true;
}
Now, with all that extra stuff encapsulated in methods, our main code becomes a little more readable, there's less chance for errors, and when there are errors, it's more clear what they are:
private static void Main()
{
int[] myArray = new int[10];
while (true)
{
int enteredNumber = ShowMenu();
if (enteredNumber == 1)
{
PopulateNextArrayItem(myArray);
}
else if (enteredNumber == 9)
{
if (Login(1234, 3))
{
PrintArray(myArray);
WaitForUserInput();
}
}
}
}

Creating a loop in my guessing game

How do I repeat this code/game if the player gets the answer wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Random RandomGenerator = new Random();// random number generator
Console.WriteLine("i have thought of a number between 1 and 100");//display message
//
//
int IN_RandomNum = RandomGenerator.Next(1, 100); //the range
//loop 10 times
for (int i = 0; i < 10; i++) //for loop created
{
int IN_Guess; //guessing integer
Console.Write("{0} turns left, enter your next guess>", 10 - i);//number of turns players has
//
IN_Guess = Convert.ToInt32(Console.ReadLine()); //string to number
//Now check if guess is same as generated
if (IN_Guess == IN_RandomNum)//if guess is equal to generated number
{
Console.WriteLine("correct in {0} turns", i + 1); //if guessed correctly
break; //breaking code
}
else if (IN_Guess > IN_RandomNum) //if guess is higher than generated number
{
Console.WriteLine("Too high");// if guessed number is too high
}
else //then...
{
Console.WriteLine("Too low"); // if guessed number is too low
} if (i==8) //on last turn display this message
{
Console.WriteLine("*YOU ONLY HAVE 1 GUESS LEFT!*"); //display this message
}
}
Console.WriteLine("please press enter to quit"); //display message
Console.ReadLine();//keeps application open until enter button hit
}
}
}
Please help because i really cant figure out where to put the while loop...ive ran out of ideas
You would need to wrap everything in a while loop.
When it exits the for loop you have, it doesn't matter if they win or lose you go back to the top and start again, generating a new random number.
Before reading the code below, see my suggestion above and attempt it yourself, see if you can crack it
Something like this:
static void Main(string[] args)
{
Random RandomGenerator = new Random();// random number generator
while (true)
{
Console.WriteLine("i have thought of a number between 1 and 100");//display message
bool guessedCorrect = false;
int IN_RandomNum = RandomGenerator.Next(1, 100); //the range
//loop 10 times
for (int i = 0; i < 10; i++) //for loop created
{
int IN_Guess; //guessing integer
Console.Write("{0} turns left, enter your next guess>", 10 - i);//number of turns players has
//
IN_Guess = Convert.ToInt32(Console.ReadLine()); //string to number
//Now check if guess is same as generated
if (IN_Guess == IN_RandomNum)//if guess is equal to generated number
{
Console.WriteLine("correct in {0} turns", i + 1); //if guessed correctly
guessedCorrect = true;
break; //breaking code
}
else if (IN_Guess > IN_RandomNum) //if guess is higher than generated number
{
Console.WriteLine("Too high");// if guessed number is too high
}
else //then...
{
Console.WriteLine("Too low"); // if guessed number is too low
}
if (i == 8) //on last turn display this message
{
Console.WriteLine("*YOU ONLY HAVE 1 GUESS LEFT!*"); //display this message
}
}
if (guessedCorrect)
{
Console.WriteLine("Good job... Lets try again");
}
else
{
Console.WriteLine("Better luck next timer... here we go");
}
}
}
P.S. I lost every time i tried, your game is too hard :-(

How can I update the current line in a C# Windows Console App?

When building a Windows Console App in C#, is it possible to write to the console without having to extend a current line or go to a new line? For example, if I want to show a percentage representing how close a process is to completion, I'd just like to update the value on the same line as the cursor, and not have to put each percentage on a new line.
Can this be done with a "standard" C# console app?
If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Notice the few spaces after the number to make sure that whatever was there before is erased.
Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.
You can use Console.SetCursorPosition to set the position of the cursor and then write at the current position.
Here is an example showing a simple "spinner":
static void Main(string[] args)
{
var spin = new ConsoleSpinner();
Console.Write("Working....");
while (true)
{
spin.Turn();
}
}
public class ConsoleSpinner
{
int counter;
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("/"); counter = 0; break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); break;
}
Thread.Sleep(100);
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
Note that you will have to make sure to overwrite any existing output with new output or blanks.
Update: As it has been criticized that the example moves the cursor only back by one character, I will add this for clarification: Using SetCursorPosition you may set the cursor to any position in the console window.
Console.SetCursorPosition(0, Console.CursorTop);
will set the cursor to the beginning of the current line (or you can use Console.CursorLeft = 0 directly).
So far we have three competing alternatives for how to do this:
Console.Write("\r{0} ", value); // Option 1: carriage return
Console.Write("\b\b\b\b\b{0}", value); // Option 2: backspace
{ // Option 3 in two parts:
Console.SetCursorPosition(0, Console.CursorTop); // - Move cursor
Console.Write(value); // - Rewrite
}
I've always used Console.CursorLeft = 0, a variation on the third option, so I decided to do some tests. Here's the code I used:
public static void CursorTest()
{
int testsize = 1000000;
Console.WriteLine("Testing cursor position");
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < testsize; i++)
{
Console.Write("\rCounting: {0} ", i);
}
sw.Stop();
Console.WriteLine("\nTime using \\r: {0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
int top = Console.CursorTop;
for (int i = 0; i < testsize; i++)
{
Console.SetCursorPosition(0, top);
Console.Write("Counting: {0} ", i);
}
sw.Stop();
Console.WriteLine("\nTime using CursorLeft: {0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
Console.Write("Counting: ");
for (int i = 0; i < testsize; i++)
{
Console.Write("\b\b\b\b\b\b\b\b{0,8}", i);
}
sw.Stop();
Console.WriteLine("\nTime using \\b: {0}", sw.ElapsedMilliseconds);
}
On my machine, I get the following results:
Backspaces: 25.0 seconds
Carriage Returns: 28.7 seconds
SetCursorPosition: 49.7 seconds
Additionally, SetCursorPosition caused noticeable flicker that I didn't observe with either of the alternatives. So, the moral is to use backspaces or carriage returns when possible, and thanks for teaching me a faster way to do this, SO!
Update: In the comments, Joel suggests that SetCursorPosition is constant with respect to the distance moved while the other methods are linear. Further testing confirms that this is the case, however constant time and slow is still slow. In my tests, writing a long string of backspaces to the console is faster than SetCursorPosition until somewhere around 60 characters. So backspace is faster for replacing portions of the line shorter than 60 characters (or so), and it doesn't flicker, so I'm going to stand by my initial endorsement of \b over \r and SetCursorPosition.
You can use the \b (backspace) escape sequence to backup a particular number of characters on the current line. This just moves the current location, it does not remove the characters.
For example:
string line="";
for(int i=0; i<100; i++)
{
string backup=new string('\b',line.Length);
Console.Write(backup);
line=string.Format("{0}%",i);
Console.Write(line);
}
Here, line is the percentage line to write to the console. The trick is to generate the correct number of \b characters for the previous output.
The advantage of this over the \r approach is that if works even if your percentage output is not at the beginning of the line.
\r is used for these scenarios.
\r represents a carriage return which means the cursor returns to the start of the line.
That's why Windows uses \n\r as its new line marker.
\n moves you down a line, and \r returns you to the start of the line.
I just had to play with the divo's ConsoleSpinner class. Mine is nowhere near as concise, but it just didn't sit well with me that users of that class have to write their own while(true) loop. I'm shooting for an experience more like this:
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
And I realized it with the code below. Since I don't want my Start() method to block, I don't want the user to have to worry about writing a while(spinFlag) -like loop, and I want to allow multiple spinners at the same time I had to spawn a separate thread to handle the spinning. And that means the code has to be a lot more complicated.
Also, I haven't done that much multi-threading so it's possible (likely even) that I've left a subtle bug or three in there. But it seems to work pretty well so far:
public class ConsoleSpinner : IDisposable
{
public ConsoleSpinner()
{
CursorLeft = Console.CursorLeft;
CursorTop = Console.CursorTop;
}
public ConsoleSpinner(bool start)
: this()
{
if (start) Start();
}
public void Start()
{
// prevent two conflicting Start() calls ot the same instance
lock (instanceLocker)
{
if (!running )
{
running = true;
turner = new Thread(Turn);
turner.Start();
}
}
}
public void StartHere()
{
SetPosition();
Start();
}
public void Stop()
{
lock (instanceLocker)
{
if (!running) return;
running = false;
if (! turner.Join(250))
turner.Abort();
}
}
public void SetPosition()
{
SetPosition(Console.CursorLeft, Console.CursorTop);
}
public void SetPosition(int left, int top)
{
bool wasRunning;
//prevent other start/stops during move
lock (instanceLocker)
{
wasRunning = running;
Stop();
CursorLeft = left;
CursorTop = top;
if (wasRunning) Start();
}
}
public bool IsSpinning { get { return running;} }
/* --- PRIVATE --- */
private int counter=-1;
private Thread turner;
private bool running = false;
private int rate = 100;
private int CursorLeft;
private int CursorTop;
private Object instanceLocker = new Object();
private static Object console = new Object();
private void Turn()
{
while (running)
{
counter++;
// prevent two instances from overlapping cursor position updates
// weird things can still happen if the main ui thread moves the cursor during an update and context switch
lock (console)
{
int OldLeft = Console.CursorLeft;
int OldTop = Console.CursorTop;
Console.SetCursorPosition(CursorLeft, CursorTop);
switch (counter)
{
case 0: Console.Write("/"); break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); counter = -1; break;
}
Console.SetCursorPosition(OldLeft, OldTop);
}
Thread.Sleep(rate);
}
lock (console)
{ // clean up
int OldLeft = Console.CursorLeft;
int OldTop = Console.CursorTop;
Console.SetCursorPosition(CursorLeft, CursorTop);
Console.Write(' ');
Console.SetCursorPosition(OldLeft, OldTop);
}
}
public void Dispose()
{
Stop();
}
}
Explicitly using a Carrage Return (\r) at the beginning of the line rather than (implicitly or explicitly) using a New Line (\n) at the end should get what you want. For example:
void demoPercentDone() {
for(int i = 0; i < 100; i++) {
System.Console.Write( "\rProcessing {0}%...", i );
System.Threading.Thread.Sleep( 1000 );
}
System.Console.WriteLine();
}
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
From the Console docs in MSDN:
You can solve this problem by setting
the TextWriter.NewLine property of the
Out or Error property to another line
termination string. For example, the
C# statement, Console.Error.NewLine =
"\r\n\r\n";, sets the line termination
string for the standard error output
stream to two carriage return and line
feed sequences. Then you can
explicitly call the WriteLine method
of the error output stream object, as
in the C# statement,
Console.Error.WriteLine();
So - I did this:
Console.Out.Newline = String.Empty;
Then I am able to control the output myself;
Console.WriteLine("Starting item 1:");
Item1();
Console.WriteLine("OK.\nStarting Item2:");
Another way of getting there.
This works if you want to make generating files look cool .
int num = 1;
var spin = new ConsoleSpinner();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("");
while (true)
{
spin.Turn();
Console.Write("\r{0} Generating Files ", num);
num++;
}
And this is the method that i got from some answer below and modified it
public class ConsoleSpinner
{
int counter;
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("."); counter = 0; break;
case 1: Console.Write(".."); break;
case 2: Console.Write("..."); break;
case 3: Console.Write("...."); break;
case 4: Console.Write("\r"); break;
}
Thread.Sleep(100);
Console.SetCursorPosition(23, Console.CursorTop);
}
}
Here's another one :D
class Program
{
static void Main(string[] args)
{
Console.Write("Working... ");
int spinIndex = 0;
while (true)
{
// obfuscate FTW! Let's hope overflow is disabled or testers are impatient
Console.Write("\b" + #"/-\|"[(spinIndex++) & 3]);
}
}
}
If you want update one line, but the information is too long to show on one line, it may need some new lines. I've encountered this problem, and below is one way to solve this.
public class DumpOutPutInforInSameLine
{
//content show in how many lines
int TotalLine = 0;
//start cursor line
int cursorTop = 0;
// use to set character number show in one line
int OneLineCharNum = 75;
public void DumpInformation(string content)
{
OutPutInSameLine(content);
SetBackSpace();
}
static void backspace(int n)
{
for (var i = 0; i < n; ++i)
Console.Write("\b \b");
}
public void SetBackSpace()
{
if (TotalLine == 0)
{
backspace(OneLineCharNum);
}
else
{
TotalLine--;
while (TotalLine >= 0)
{
backspace(OneLineCharNum);
TotalLine--;
if (TotalLine >= 0)
{
Console.SetCursorPosition(OneLineCharNum, cursorTop + TotalLine);
}
}
}
}
private void OutPutInSameLine(string content)
{
//Console.WriteLine(TotalNum);
cursorTop = Console.CursorTop;
TotalLine = content.Length / OneLineCharNum;
if (content.Length % OneLineCharNum > 0)
{
TotalLine++;
}
if (TotalLine == 0)
{
Console.Write("{0}", content);
return;
}
int i = 0;
while (i < TotalLine)
{
int cNum = i * OneLineCharNum;
if (i < TotalLine - 1)
{
Console.WriteLine("{0}", content.Substring(cNum, OneLineCharNum));
}
else
{
Console.Write("{0}", content.Substring(cNum, content.Length - cNum));
}
i++;
}
}
}
class Program
{
static void Main(string[] args)
{
DumpOutPutInforInSameLine outPutInSameLine = new DumpOutPutInforInSameLine();
outPutInSameLine.DumpInformation("");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
//need several lines
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbb");
}
}
i was looking for same solution in vb.net and i found this one and it's great.
however as #JohnOdom suggested a better way to handle the blanks space if previous one is larger than current one..
i make a function in vb.net and thought someone could get helped ..
here is my code:
Private Sub sPrintStatus(strTextToPrint As String, Optional boolIsNewLine As Boolean = False)
REM intLastLength is declared as public variable on global scope like below
REM intLastLength As Integer
If boolIsNewLine = True Then
intLastLength = 0
End If
If intLastLength > strTextToPrint.Length Then
Console.Write(Convert.ToChar(13) & strTextToPrint.PadRight(strTextToPrint.Length + (intLastLength - strTextToPrint.Length), Convert.ToChar(" ")))
Else
Console.Write(Convert.ToChar(13) & strTextToPrint)
End If
intLastLength = strTextToPrint.Length
End Sub
I was doing a search for this to see if the solution I wrote could be optimised for speed. What I wanted was a countdown timer, not just updating the current line.
Here's what I came up with. Might be useful to someone
int sleepTime = 5 * 60; // 5 minutes
for (int secondsRemaining = sleepTime; secondsRemaining > 0; secondsRemaining --)
{
double minutesPrecise = secondsRemaining / 60;
double minutesRounded = Math.Round(minutesPrecise, 0);
int seconds = Convert.ToInt32((minutesRounded * 60) - secondsRemaining);
Console.Write($"\rProcess will resume in {minutesRounded}:{String.Format("{0:D2}", -seconds)} ");
Thread.Sleep(1000);
}
Console.WriteLine("");
Inspired by #E.Lahu Solution, implementation of a bar progress with percentage.
public class ConsoleSpinner
{
private int _counter;
public void Turn(Color color, int max, string prefix = "Completed", string symbol = "■",int position = 0)
{
Console.SetCursorPosition(0, position);
Console.Write($"{prefix} {ComputeSpinner(_counter, max, symbol)}", color);
_counter = _counter == max ? 0 : _counter + 1;
}
public string ComputeSpinner(int nmb, int max, string symbol)
{
var spinner = new StringBuilder();
if (nmb == 0)
return "\r ";
spinner.Append($"[{nmb}%] [");
for (var i = 0; i < max; i++)
{
spinner.Append(i < nmb ? symbol : ".");
}
spinner.Append("]");
return spinner.ToString();
}
}
public static void Main(string[] args)
{
var progressBar= new ConsoleSpinner();
for (int i = 0; i < 1000; i++)
{
progressBar.Turn(Color.Aqua,100);
Thread.Sleep(1000);
}
}
Here is my take on s soosh's and 0xA3's answers.
It can update the console with user messages while updating the spinner and has an elapsed time indicator aswell.
public class ConsoleSpiner : IDisposable
{
private static readonly string INDICATOR = "/-\\|";
private static readonly string MASK = "\r{0} {1:c} {2}";
int counter;
Timer timer;
string message;
public ConsoleSpiner() {
counter = 0;
timer = new Timer(200);
timer.Elapsed += TimerTick;
}
public void Start() {
timer.Start();
}
public void Stop() {
timer.Stop();
counter = 0;
}
public string Message {
get { return message; }
set { message = value; }
}
private void TimerTick(object sender, ElapsedEventArgs e) {
Turn();
}
private void Turn() {
counter++;
var elapsed = TimeSpan.FromMilliseconds(counter * 200);
Console.Write(MASK, INDICATOR[counter % 4], elapsed, this.Message);
}
public void Dispose() {
Stop();
timer.Elapsed -= TimerTick;
this.timer.Dispose();
}
}
usage is something like this:
class Program
{
static void Main(string[] args)
{
using (var spinner = new ConsoleSpiner())
{
spinner.Start();
spinner.Message = "About to do some heavy staff :-)"
DoWork();
spinner.Message = "Now processing other staff".
OtherWork();
spinner.Stop();
}
Console.WriteLine("COMPLETED!!!!!\nPress any key to exit.");
}
}
The SetCursorPosition method works in multi-threading scenario, where the other two methods don't

Categories

Resources