ฉันจะอัพเดตบรรทัดปัจจุบันในแอพ C # Windows Console ได้อย่างไร?


507

เมื่อสร้าง Windows Console App ใน C # เป็นไปได้ไหมที่จะเขียนไปยังคอนโซลโดยไม่ต้องขยายบรรทัดปัจจุบันหรือไปที่บรรทัดใหม่? ตัวอย่างเช่นหากฉันต้องการแสดงเปอร์เซ็นต์ที่แสดงว่ากระบวนการเสร็จสิ้นแล้วฉันต้องการอัปเดตค่าในบรรทัดเดียวกับเคอร์เซอร์และไม่ต้องใส่แต่ละเปอร์เซ็นต์ในบรรทัดใหม่

สามารถทำได้ด้วยแอปคอนโซล "มาตรฐาน" C # หรือไม่


หากคุณสนใจอินเทอร์เฟซบรรทัดคำสั่งที่ยอดเยี่ยมจริงๆคุณควรตรวจสอบคำสาป / ncurses
Charles Addis

@CharlesAddis แต่ไม่ได้ใช้คำสาป / ncurses ใน C ++ เท่านั้น?
Xam

คำตอบ:


783

หากคุณพิมพ์"\r"ไปที่คอนโซลเท่านั้นเคอร์เซอร์จะกลับไปที่จุดเริ่มต้นของบรรทัดปัจจุบันจากนั้นคุณสามารถเขียนใหม่ได้ สิ่งนี้ควรทำเคล็ดลับ:

for(int i = 0; i < 100; ++i)
{
    Console.Write("\r{0}%   ", i);
}

สังเกตุช่องว่างเล็ก ๆ หลังจากตัวเลขเพื่อให้แน่ใจว่าสิ่งที่เคยมีมาก่อนถูกลบ
โปรดสังเกตการใช้Write()แทนWriteLine()เนื่องจากคุณไม่ต้องการเพิ่ม "\ n" ที่ท้ายบรรทัด


7
สำหรับ (int i = 0; i <= 100; ++ i) จะไปที่ 100%
Nicolas Tyler

13
คุณจัดการอย่างไรเมื่อการเขียนก่อนหน้ายาวกว่าการเขียนใหม่ มีวิธีรับความกว้างของคอนโซลและแผ่นบรรทัดด้วยช่องว่างหรือไม่
Drew Chapin

6
@Ruciferre ปิดส่วนหัวของฉันฉันสามารถคิดสองคำตอบสำหรับคำถามของคุณ ทั้งสองเกี่ยวข้องกับการบันทึกเอาต์พุตปัจจุบันเป็นสตริงก่อนและเติมด้วยจำนวนชุดอักขระเช่นนี้: Console.Write ("\ r {0}", strOutput.PadRight (nPaddingCount, '')); "nPaddingCount" สามารถเป็นตัวเลขที่คุณตั้งค่าตัวคุณเองหรือคุณสามารถติดตามเอาท์พุทก่อนหน้าและตั้งค่า nPaddingCount เป็นความแตกต่างของความยาวระหว่างเอาท์พุทก่อนหน้าและปัจจุบันบวกความยาวเอาท์พุทปัจจุบัน ถ้า nPaddingCount เป็นค่าลบคุณจะไม่ต้องใช้ PadRight เว้นแต่ว่าคุณใช้ abs (prev.len - curr.len)
John Odom

1
@malgm รหัสที่จัดดี หากมีเธรดหนึ่งโหลที่สามารถเขียนไปยังคอนโซลได้ทุกเวลาที่ต้องการนั่นจะทำให้คุณมีปัญหาไม่ว่าคุณจะเขียนบรรทัดใหม่หรือไม่ก็ตาม
ทำเครื่องหมาย

2
@ JohnOdom คุณจะต้องรักษาความยาวเอาท์พุทก่อนหน้านี้ (ไม่ได้เสริม) แล้วฟีดที่เป็นอาร์กิวเมนต์แรกในการPadRight(บันทึกสตริง unpadded หรือความยาวครั้งแรกของหลักสูตร)
Jesper Matthiesen

254

คุณสามารถใช้Console.SetCursorPositionเพื่อกำหนดตำแหน่งของเคอร์เซอร์แล้วเขียนที่ตำแหน่งปัจจุบัน

นี่คือตัวอย่างที่แสดง "สปินเนอร์" อย่างง่าย:

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);
    }
}

โปรดทราบว่าคุณจะต้องตรวจสอบให้แน่ใจว่าได้เขียนทับเอาต์พุตที่มีอยู่ด้วยเอาต์พุตหรือช่องว่างใหม่

อัปเดต: เนื่องจากมีการวิพากษ์วิจารณ์ว่าตัวอย่างย้ายเคอร์เซอร์ไปข้างหลังด้วยตัวอักษรตัวเดียวฉันจะเพิ่มสิ่งนี้เพื่อความกระจ่าง: การใช้SetCursorPositionคุณอาจตั้งค่าเคอร์เซอร์ไปยังตำแหน่งใด ๆ ในหน้าต่างคอนโซล

Console.SetCursorPosition(0, Console.CursorTop);

จะตั้งค่าเคอร์เซอร์เป็นจุดเริ่มต้นของบรรทัดปัจจุบัน (หรือคุณสามารถใช้Console.CursorLeft = 0โดยตรง)


8
ปัญหาอาจแก้ไขได้โดยใช้ \ r แต่การใช้SetCursorPosition(หรือCursorLeft) ช่วยให้มีความยืดหยุ่นมากขึ้นเช่นไม่เขียนที่จุดเริ่มต้นของบรรทัดเลื่อนขึ้นในหน้าต่าง ฯลฯ ดังนั้นจึงเป็นวิธีการทั่วไปที่สามารถใช้เช่นเอาต์พุต แถบความคืบหน้าที่กำหนดเองหรือกราฟิก ASCII
Dirk Vollmar

14
+1 สำหรับการใช้คำพูดอย่างละเอียดและเหนือกว่าการปฏิบัติหน้าที่ ขอขอบคุณ
Copas

1
+1 สำหรับแสดงวิธีการที่แตกต่างออกไป ทุกคนแสดงให้เห็น \ r และถ้า OP เป็นเพียงการปรับปรุงเปอร์เซ็นต์ด้วยสิ่งนี้เขาสามารถอัปเดตค่าโดยไม่ต้องเขียนทั้งบรรทัดใหม่ OP ไม่เคยบอกว่าจริง ๆ แล้วเขาต้องการย้ายไปยังจุดเริ่มต้นของบรรทัดเพียงว่าเขาต้องการอัปเดตบางอย่างในบรรทัดเดียวกันกับเคอร์เซอร์
Andy

1
ความยืดหยุ่นที่เพิ่มขึ้นของ SetCursorPosition นั้นมาพร้อมกับความเร็วเล็กน้อยและเคอร์เซอร์จะกะพริบหากสังเกตเห็นว่าห่วงนั้นยาวพอที่ผู้ใช้จะสังเกตเห็น ดูความคิดเห็นทดสอบของฉันด้านล่าง
Kevin

5
นอกจากนี้ยืนยันว่าความยาวบรรทัดไม่ทำให้คอนโซลตัดกับบรรทัดถัดไปหรือคุณอาจได้รับปัญหาเกี่ยวกับเนื้อหาที่ทำงานลงในหน้าต่างคอนโซลอย่างไรก็ตาม
Mandrake

84

จนถึงตอนนี้เรามีทางเลือกการแข่งขันสามทางสำหรับวิธีการ:

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
}

ฉันใช้Console.CursorLeft = 0รูปแบบตัวเลือกที่สามอยู่เสมอดังนั้นฉันจึงตัดสินใจทำการทดสอบ นี่คือรหัสที่ฉันใช้:

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);
}

บนเครื่องของฉันฉันได้รับผลลัพธ์ต่อไปนี้:

  • Backspaces: 25.0 วินาที
  • การคืนรถ: 28.7 วินาที
  • SetCursorPosition: 49.7 วินาที

นอกจากนี้ยังSetCursorPositionทำให้เกิดการสั่นไหวที่สังเกตเห็นได้ซึ่งฉันไม่ได้สังเกตด้วยทางเลือกใดวิธีหนึ่ง ดังนั้นคุณธรรมคือการใช้ backspaces หรือ carriage return เมื่อเป็นไปได้และขอขอบคุณสำหรับการสอนวิธีที่เร็วกว่านี้ให้ฉันดังนั้น!


อัปเดต : ในความคิดเห็นโจเอลแนะนำว่า SetCursorPosition เป็นค่าคงที่ตามระยะทางที่เคลื่อนที่ในขณะที่วิธีอื่นเป็นแบบเส้นตรง การทดสอบเพิ่มเติมยืนยันว่าเป็นกรณีนี้อย่างไรก็ตามเวลาคงที่และช้ายังคงช้า ในการทดสอบของฉันการเขียนแบ็คสเปซหลังยาวไปยังคอนโซลนั้นเร็วกว่า SetCursorPosition จนกระทั่งมีประมาณ 60 ตัวอักษร ดังนั้น Backspace เร็วเพื่อทดแทนส่วนของเส้นที่สั้นกว่า 60 ตัวอักษร (หรือมากกว่านั้น) และมันไม่ได้สั่นไหวดังนั้นฉันจะยืนตามการรับรองของฉันเริ่มต้นของ \ ขมากกว่า \ r SetCursorPositionและ


4
ประสิทธิภาพของการดำเนินการที่เป็นปัญหาไม่ควรเกิดขึ้นจริง มันควรจะเกิดขึ้นเร็วเกินไปที่ผู้ใช้จะสังเกตเห็น microptimisation ที่ไม่จำเป็นนั้นไม่ดี
Malfist

@ มัลลิสต์: ผู้ใช้อาจสังเกตเห็นหรือไม่สังเกตก็ได้ ขณะที่ฉันเพิ่มในการแก้ไขข้างต้น (ก่อนที่ฉันจะเห็นความคิดเห็นของคุณ) SetCursorPosition แนะนำการสั่นไหวและใช้เวลาเกือบสองเท่าของตัวเลือกอื่น ๆ
Kevin

1
ฉันยอมรับว่ามันเป็นการเพิ่มประสิทธิภาพขนาดเล็ก (ใช้งานเป็นล้านครั้งและใช้เวลา 50 วินาทีก็ยังคงมีเวลาน้อยมาก) +1 สำหรับผลลัพธ์และแน่นอนว่ามันมีประโยชน์มากที่จะรู้
Andy

6
เกณฑ์มาตรฐานนั้นมีข้อบกพร่องพื้นฐาน เป็นไปได้ที่เวลา SetCursorPosition () จะเท่ากันไม่ว่าเคอร์เซอร์จะเลื่อนไปไกลแค่ไหนในขณะที่ตัวเลือกอื่นนั้นจะแตกต่างกันไปตามจำนวนตัวอักษรที่คอนโซลต้องดำเนินการ
Joel Coehoorn

1
นี่เป็นผลรวมที่ดีมากของตัวเลือกต่าง ๆ ที่มีอยู่ อย่างไรก็ตามฉันยังเห็นการกะพริบเมื่อใช้ \ r เห็นได้ชัดว่า \ b ไม่มีการกะพริบเนื่องจากข้อความการแก้ไข ("การนับ:") ไม่ได้ถูกเขียนใหม่ นอกจากนี้คุณยังจะได้รับริบหรี่ถ้าคุณเพิ่ม \ b เพิ่มเติมและเขียนข้อความแก้ไขตามที่เกิดขึ้นกับ \ b และ SetCursorPosition เกี่ยวกับคำพูดของโจเอล: โจเอลนั้นถูกต้อง แต่ \ r จะยังคงเหนือกว่า SetCursorPosition ในบรรทัดที่ยาวมาก แต่ความแตกต่างลดน้อยลง
Dirk Vollmar

27

คุณสามารถใช้ลำดับหลบหนี\ b (backspace) เพื่อสำรองข้อมูลจำนวนอักขระเฉพาะในบรรทัดปัจจุบัน นี่เป็นเพียงการย้ายตำแหน่งปัจจุบันมันไม่ได้ลบตัวละคร

ตัวอย่างเช่น:

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);
}

นี่คือบรรทัดเป็นเปอร์เซ็นต์ในการเขียนไปยังคอนโซล เคล็ดลับคือการสร้างจำนวน\ bอักขระที่ถูกต้องสำหรับเอาต์พุตก่อนหน้า

ข้อได้เปรียบของวิธีนี้คือ\ถ้าวิธีนี้ใช้ได้ผลแม้ว่าเปอร์เซ็นต์ของคุณจะไม่อยู่ที่จุดเริ่มต้นของบรรทัด


1
+1 ผลัดกันนี้ออกมาเป็นวิธีที่เร็วที่สุดที่นำเสนอ (ดูความคิดเห็นการทดสอบของฉันด้านล่าง)
เควิน

19

\rใช้สำหรับสถานการณ์เหล่านี้
\r แสดงถึงการขึ้นบรรทัดใหม่ซึ่งหมายถึงเคอร์เซอร์จะกลับไปที่จุดเริ่มต้นของบรรทัด
นั่นเป็นเหตุผลว่าทำไม Windows จึงใช้\n\rเป็นตัวระบุบรรทัดใหม่
\nเลื่อนคุณลงบรรทัดและ\rกลับไปที่จุดเริ่มต้นของบรรทัด


22
ยกเว้นจริง ๆ แล้ว \ r \ n
Joel Mueller

14

ฉันแค่ต้องเล่นกับConsoleSpinnerชั้นเรียนของนักร้อง ของฉันไม่มีที่ใดที่จะรัดกุม แต่มันก็ไม่ได้ดีกับฉันว่าผู้ใช้ของชั้นนั้นต้องเขียนwhile(true)วงของตัวเอง ฉันถ่ายทำเพื่อประสบการณ์มากขึ้นเช่นนี้:

static void Main(string[] args)
{
    Console.Write("Working....");
    ConsoleSpinner spin = new ConsoleSpinner();
    spin.Start();

    // Do some work...

    spin.Stop(); 
}

และฉันก็รู้ด้วยรหัสด้านล่าง เนื่องจากฉันไม่ต้องการStart()วิธีการบล็อกฉันไม่ต้องการให้ผู้ใช้ต้องกังวลเกี่ยวกับการเขียนwhile(spinFlag)ลูปเหมือนและฉันต้องการอนุญาตให้สปินเนอร์หลายคนในเวลาเดียวกันฉันต้องวางไข่เธรดแยกต่างหากเพื่อจัดการ การปั่นด้าย และนั่นหมายความว่ารหัสจะต้องมีความซับซ้อนมากขึ้น

นอกจากนี้ฉันยังไม่ได้ทำมัลติเธรดที่มากดังนั้นจึงเป็นไปได้ (น่าจะเป็นไปได้) ว่าฉันได้ทิ้งบั๊กที่บอบบางหรือสามอันในนั้น แต่ดูเหมือนว่าจะทำงานได้ดีจนถึงตอนนี้:

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();
    }
}

การปรับเปลี่ยนที่ดีแม้ว่ารหัสตัวอย่างไม่ใช่ของฉัน มันมาจากบล็อกของ Brad Abrams (ดูลิงก์ในคำตอบของฉัน) ฉันคิดว่ามันถูกเขียนเป็นตัวอย่างง่ายๆที่แสดงให้เห็น SetCursorPosition Btw ฉันประหลาดใจแน่นอน (ในทางบวก) เกี่ยวกับการสนทนาเริ่มต้นเกี่ยวกับสิ่งที่ฉันคิดว่าเป็นเพียงตัวอย่างง่ายๆ นั่นเป็นเหตุผลที่ฉันรักเว็บไซต์นี้ :-)
เดิร์กโวลมาร์

4

การใช้ Carrage Return (\ r) ที่จุดเริ่มต้นของบรรทัดอย่างชัดเจนแทนที่จะใช้ (โดยนัยหรือโดยชัดแจ้ง) โดยใช้บรรทัดใหม่ (\ n) ในตอนท้ายควรได้รับสิ่งที่คุณต้องการ ตัวอย่างเช่น:

void demoPercentDone() {
    for(int i = 0; i < 100; i++) {
        System.Console.Write( "\rProcessing {0}%...", i );
        System.Threading.Thread.Sleep( 1000 );
    }
    System.Console.WriteLine();    
}

-1 คำถามที่ถามสำหรับ C # ฉันเขียนมันใน C # และคุณจะเปลี่ยนกลับไป F #
Malfist

ดูเหมือนความขัดแย้งในการแก้ไขมากกว่าที่จะเปลี่ยน C # ของคุณกลับไปเป็น F # การเปลี่ยนแปลงของเขาคือหนึ่งนาทีหลังจากคุณและเพ่งความสนใจไปที่ sprintf
Andy

ขอบคุณสำหรับการแก้ไข ฉันมักจะใช้โหมดการโต้ตอบ F # เพื่อทดสอบสิ่งต่าง ๆ และคิดว่าส่วนที่สำคัญคือการเรียก BCL ซึ่งเหมือนกันใน C #
James Hugard


1

จากเอกสารคอนโซลใน MSDN:

คุณสามารถแก้ปัญหานี้ได้โดยการตั้งค่าคุณสมบัติ TextWriter.NewLine ของคุณสมบัติ Out หรือ Error ให้เป็นสายอักขระการเลิกจ้างบรรทัดอื่น ตัวอย่างเช่นคำสั่ง C #, Console.Error.NewLine = "\ r \ n \ r \ n";, ตั้งค่าสตริงการสิ้นสุดบรรทัดสำหรับสตรีมเอาต์พุตข้อผิดพลาดมาตรฐานเป็นลำดับการส่งคืนค่าขนส่งสองและลำดับการป้อนบรรทัด จากนั้นคุณสามารถเรียกใช้เมธอด WriteLine ของอ็อบเจ็กต์สตรีมข้อผิดพลาดเอาต์พุตเช่นในคำสั่ง C #, Console.Error.WriteLine ();

ดังนั้น - ฉันทำสิ่งนี้:

Console.Out.Newline = String.Empty;

จากนั้นฉันสามารถควบคุมผลลัพธ์ได้ด้วยตนเอง

Console.WriteLine("Starting item 1:");
    Item1();
Console.WriteLine("OK.\nStarting Item2:");

อีกวิธีในการเดินทาง


คุณสามารถใช้ Console.Write () เพื่อจุดประสงค์เดียวกันโดยไม่ต้องกำหนดคุณสมบัติ NewLine ใหม่ ...
Radosław Gers

1

วิธีนี้ใช้ได้ผลถ้าคุณต้องการสร้างไฟล์ให้ดูดี

                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++;
                }

และนี่คือวิธีการที่ฉันได้จากคำตอบด้านล่างและแก้ไขมัน

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);
        }
    }

0

นี่คืออีกหนึ่ง: 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]);
        }
    }
}

0

หากคุณต้องการอัปเดตหนึ่งบรรทัด แต่ข้อมูลยาวเกินกว่าที่จะแสดงในหนึ่งบรรทัดอาจต้องมีบางบรรทัดใหม่ ฉันพบปัญหานี้แล้วและด้านล่างเป็นวิธีหนึ่งในการแก้ไขปัญหานี้

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");

    }
}

0

ฉันกำลังมองหาวิธีการแก้ปัญหาเดียวกันใน vb.net และฉันพบนี้และมันยอดเยี่ยม

อย่างไรก็ตามตามที่ @JohnOdom แนะนำวิธีที่ดีกว่าในการจัดการช่องว่างถ้าพื้นที่ก่อนหน้านั้นใหญ่กว่าพื้นที่ปัจจุบัน ..

ฉันสร้างฟังก์ชั่นใน vb.net และคิดว่ามีคนได้รับความช่วยเหลือ ..

นี่คือรหัสของฉัน:

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

ที่นี่คุณสามารถใช้คุณลักษณะ VB Static intLastLength As Integerของตัวแปรคงท้องถิ่น:
Mark Hurd

0

ฉันกำลังค้นหาสิ่งนี้เพื่อดูว่าโซลูชันที่ฉันเขียนสามารถปรับให้เหมาะสมกับความเร็วได้หรือไม่ สิ่งที่ฉันต้องการคือตัวจับเวลานับถอยหลังไม่ใช่แค่อัปเดตบรรทัดปัจจุบัน นี่คือสิ่งที่ฉันมาด้วย อาจเป็นประโยชน์กับใครบางคน

            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("");

0

แรงบันดาลใจจาก @ E.Lahu Solution การนำแถบความก้าวหน้าไปใช้ด้วยเปอร์เซ็นต์

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);
        }
    }

0

นี่คือคำตอบของฉันใน soosh's และ 0xA3 สามารถอัพเดตคอนโซลด้วยข้อความผู้ใช้ขณะอัพเดตสปินเนอร์และมีตัวบ่งชี้เวลาที่ผ่านไปเช่นกัน

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();
    }
}

การใช้งานเป็นแบบนี้:

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.");

    }
}

-1

SetCursorPositionวิธีการทำงานในสถานการณ์แบบมัลติเธรดที่อีกสองวิธีทำไม่ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.