2009-03-18 42 views
30

Trong hầu hết các vỏ hiện đại, bạn có thể nhấn mũi tên lên và xuống và nó sẽ đặt, tại dấu nhắc, các lệnh trước đó mà bạn đã thực hiện. Câu hỏi của tôi là, nó hoạt động thế nào ?!Làm thế nào để ghi đè lên stdout trong C

Dường như với tôi rằng trình bao bằng cách nào đó thao tác với thiết bị xuất chuẩn để ghi đè lên những gì nó đã viết?

Tôi nhận thấy rằng các chương trình như wget cũng làm việc này. Có ai có bất kỳ ý tưởng làm thế nào họ làm điều đó?

Trả lời

40

Nó không thao túng thiết bị đầu cuối - nó ghi đè các ký tự đã được thiết bị đầu cuối hiển thị.

Hãy thử điều này:

#include <stdio.h> 
#include <unistd.h> 
static char bar[] = "=======================================" 
        "======================================>"; 
int main() { 
    int i; 
    for (i = 77; i >= 0; i--) { 
     printf("[%s]\r", &bar[i]); 
     fflush(stdout); 
     sleep(1); 
    } 
    printf("\n"); 
    return 0; 
} 

Đó là khá gần với wget 's đầu ra, phải không? \r là một vận chuyển trở lại, mà thiết bị đầu cuối thông dịch là "di chuyển con trỏ trở lại đầu dòng hiện tại".

vỏ của bạn, nếu nó bash, sử dụng GNU Readline library, cung cấp nhiều chức năng tổng quát hơn, bao gồm các loại thiết bị đầu cuối phát hiện, quản lý lịch sử, tổ hợp phím có thể lập trình vv

hơn Một điều - khi nghi ngờ, các nguồn cho wget của bạn, vỏ của bạn, vv đều có sẵn.

2

Nó được thực hiện với thư viện readline ... Tôi không chắc nó hoạt động như thế nào ở hậu trường nhưng tôi không nghĩ rằng nó có liên quan gì đến stdout hoặc stream. Tôi nghi ngờ readline sử dụng một số loại lệnh khó hiểu (đối với tôi, ít nhất) - có nghĩa là nó hợp tác với chương trình đầu cuối thực sự hiển thị phiên trình bao của bạn. Tôi không biết rằng bạn có thể nhận được hành vi giống như đường dây chỉ bằng cách in đầu ra.

(Hãy nghĩ về điều này: stdout có thể được chuyển hướng đến một tập tin, nhưng các phím lên/xuống mũi tên lừa không hoạt động trên các tập tin.)

21

Để ghi đè lên dòng đầu ra tiêu chuẩn hiện hành (hoặc các bộ phận của nó) sử dụng \r (hoặc \b.) Các ký tự đặc biệt \r (vận chuyển trở lại) sẽ trả lại dấu mũ vào đầu dòng, cho phép bạn ghi đè lên nó. Ký tự đặc biệt \b sẽ chỉ đưa dấu nháy trở lại một vị trí, cho phép bạn ghi đè ký tự cuối cùng, ví dụ:

#include <stdio.h> 
#include <unistd.h> 

int i; 
const char progress[] = "|/-\\"; 

for (i = 0; i < 100; i += 10) { 
    printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */ 
    fflush(stdout); 
    sleep(1); 
} 
printf("\n"); /* goes to the next line */ 
fflush(stdout); 

printf("Processing: "); 
for (i = 0; i < 100; i += 10) { 
    printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */ 
    fflush(stdout); 
    sleep(1); 
} 
printf("\n"); /* goes to the next line */ 
fflush(stdout); 

Sử dụng fflush(stdout);standard output is usually buffered và các thông tin có thể không khác được in ngay trên đầu ra hoặc thiết bị đầu cuối

+1

Có thể muốn thêm #include cho chức năng ngủ. – Prairiedogg

0

Bạn có thể sử dụng vận chuyển trở lại để mô phỏng này.

#include <stdio.h> 

int main(int argc, char* argv[]) 
{ 
    while(1) 
    { 
     printf("***********"); 
     fflush(stdout); 
     sleep(1); 
     printf("\r"); 
     printf("..........."); 
     sleep(1); 
    } 

    return 0; 
} 
11

Ngoài \ r và \ b, hãy xem ncurses để có một số kiểm soát nâng cao trên nội dung trên màn hình bảng điều khiển. (Bao gồm các cột, di chuyển xung quanh tùy tiện, vv).

0

Cách đơn giản nhất là in để stdout ký tự trả về vận chuyển ('\ r').

Con trỏ sẽ được di chuyển đến đầu dòng, cho phép bạn ghi đè nội dung của nó.

1

Chương trình thực hiện việc này bằng cách in các ký tự đặc biệt mà thiết bị đầu cuối diễn giải theo cách đặc biệt.Phiên bản đơn giản nhất của điều này là (trên hầu hết các thiết bị đầu cuối linux/unix) để in '\ r' (vận chuyển trở lại) về giá trị chuẩn thông thường, đặt lại vị trí con trỏ thành ký tự đầu tiên trong dòng hiện tại. Vì vậy, điều bạn viết tiếp theo sẽ ghi đè lên dòng bạn đã viết trước đây. Điều này có thể được sử dụng cho các chỉ số tiến độ đơn giản, ví dụ.

int i = 0; 
while (something) { 
    i++; 
    printf("\rprocessing line %i...", i); 
    ... 
} 

Nhưng có nhiều chuỗi ký tự thoát phức tạp hơn được diễn giải theo nhiều cách khác nhau. Tất cả các loại điều có thể được thực hiện với điều này, như định vị con trỏ tại một vị trí cụ thể trên màn hình hoặc thiết lập màu văn bản. Nếu hoặc như thế nào các chuỗi ký tự được giải thích phụ thuộc vào thiết bị đầu cuối của bạn, nhưng một lớp phổ biến được hỗ trợ bởi hầu hết các thiết bị đầu cuối là ansi escape sequences. Vì vậy, nếu bạn muốn chữ màu đỏ, hãy thử:

printf("Text in \033[1;31mred\033[0m\n"); 
+0

Nó không phải là trình bao xử lý các ký tự đặc biệt này, đó là thiết bị đầu cuối mà trình bao đang chạy, tức là một phiên bản của xterm, rxvt, konsole hoặc tương tự. – sleske

+0

bạn tất nhiên là đúng. – sth

5

Một chương trình đang chạy trong một thiết bị đầu cuối text/console có thể thao tác văn bản được hiển thị trong giao diện điều khiển của nó theo những cách khác nhau (làm cho văn bản in đậm, di chuyển con trỏ, màn hình rõ ràng vv). Điều này được thực hiện bằng cách in các chuỗi ký tự đặc biệt, được gọi là "escape sequences" (vì chúng thường bắt đầu bằng Escape, ASCII 27).

Nếu thiết bị đầu cuối đi tới thiết bị đầu cuối hiểu các trình tự thoát này, màn hình của thiết bị đầu cuối sẽ thay đổi tương ứng.

Nếu bạn chuyển hướng stdout vào một tệp, chuỗi thoát sẽ xuất hiện trong tệp (thường không phải là những gì bạn muốn).

Không có tiêu chuẩn hoàn chỉnh cho các trình tự thoát, nhưng hầu hết các thiết bị đầu cuối đều sử dụng các trình tự được giới thiệu bởi VT100, với nhiều phần mở rộng. Đây là những gì hầu hết các thiết bị đầu cuối dưới Unix/Linux (xterm, rxvt, konsole) và những người khác như PuTTY hiểu.

Trong thực tế, bạn sẽ không trực tiếp mã hóa chuỗi thoát vào phần mềm của bạn (mặc dù bạn có thể), nhưng sử dụng thư viện để in chúng, chẳng hạn như ncurses hoặc GNU readline được đề cập ở trên. Điều này cho phép khả năng tương thích với các loại thiết bị đầu cuối khác nhau.

Các vấn đề liên quan