2013-05-23 32 views
6

Nếu tôi muốn có một chương trình để có nhiều định dạng đầu ra văn bản, tôi có thể làm một cái gì đó như thế này:Có mã "printf" không in bất kỳ thứ gì, được sử dụng để bỏ qua một tham số không?

const char *fmtDefault = "%u x %s ($%.2f each)\n"; 
const char *fmtMultiLine = "Qty: %3u\nItem: %s\nPrice per item: $%.2f\n\n"; 
const char *fmtCSV = "%u,%s,%.2f\n"; 

const char *fmt; 
switch (which_format) { 
    case 1: fmt = fmtMultiLine; break; 
    case 2: fmt = fmtCSV; break; 
    default: fmt = fmtDefault; 
} 

printf(fmt, quantity, item_description, price); 

Kể từ khi giá được quy định cuối cùng, tôi cũng có thể thêm một điều đó không có niêm yết giá:

const char *fmtNoPrices = "%u x %s\n"; 

Nhưng nếu tôi muốn bỏ qua số lượng thay thế thì sao? Nếu tôi đã làm điều này:

const char *fmtNoQuantity = "The price of %s is $%.2f each.\n"; 

khi đó hành vi không xác định (có thể là segfault) sẽ xảy ra thay vì những gì tôi muốn. Điều này là bởi vì nó sẽ xử lý tham số đầu tiên như một con trỏ tới một chuỗi, mặc dù nó thực sự là một int không dấu. Int unsigned này rất có thể trỏ đến một cái gì đó khác với dữ liệu chuỗi hợp lệ, hoặc (nhiều khả năng, đặc biệt nếu bạn không mua hàng trăm triệu cùng một mục) một vị trí bộ nhớ không hợp lệ, dẫn đến lỗi phân đoạn.

Những gì tôi muốn biết là nếu có một mã tôi có thể đặt ở đâu đó (%Z trong ví dụ này) để nói với nó để bỏ qua tham số đó, như thế này:

const char *fmtNoQuantity = "%ZThe price of %s is $%.2f each."; 
+1

scanf() có thể sử dụng dấu hoa thị, nhưng không thể in được IIRC printf(). Tôi cũng đã thử sử dụng '.0' độ chính xác specifiers, nhưng điều này dường như chỉ hoạt động trên chuỗi ('% .0s' sẽ hiển thị không có gì, nhưng có thể vẫn dereference con trỏ nếu không null) – Medinoc

+0

Bạn nên sử dụng bộ riêng biệt của các cuộc gọi với danh sách đối số riêng biệt, tôi nghĩ. Làm cách khác làm cho quốc tế hóa (I18N) khó khăn hơn nhiều. –

Trả lời

4

Đối %s giá trị, có một “null” printf() code: %.0s.

Bạn có thể đạt được một giải pháp chung qua:

Khi có thể, sắp xếp lại để non-%s giá trị là cuối cùng, và sau đó theo chỉ định chuỗi định dạng.

Yêu thích của tôi dành cho bạn là có 3 cuộc gọi printf() riêng biệt, một cho mỗi giá trị sử dụng định dạng riêng của nó. Khi giá trị là không cần thiết, chỉ cần cung cấp một chuỗi định dạng không có specifiers.

const char * Format1q = ""; 
const char * Format1id = "The price of %s"; 
const char * Format1p = " is $%.2f each.\n"; 
... 
printf(Format1q, quantity); 
printf(Format1id, item_description); 
printf(Format1p, price); 

giải pháp Weird:

Đối với các giá trị khác mà có cùng kích thước bạn có thể cố gắng hành vi Undefined của cũng sử dụng %.0s. (làm việc với một số mẫu trong gcc 4.5.3, những người biết trong các trình biên dịch khác hoặc tương lai.)

Đối với các giá trị khác là N x có cùng kích thước với kích thước con trỏ, bạn có thể thử hành vi không xác định cũng sử dụng %.0s N lần. (làm việc với một số mẫu trong gcc 4.5.3, ai biết trong các trình biên dịch khác hoặc tương lai.)

+0

Hãy coi chừng quốc tế hóa (I18N). Các đoạn câu kết hợp tốt bằng tiếng Anh có thể là một thảm họa trong bản dịch. –

+0

Đồng ý về các vấn đề quốc tế. Ngoài ra, với thứ tự cố định của các giá trị, chúng ta có thể sắp xếp lại các giá trị cho đến một nền văn hóa khác nhau và vượt quá các giải pháp đã trình bày. – chux

+1

Ký hiệu '% 1 $ s' có thể sắp xếp lại (và thậm chí tái sử dụng) các tham số, nhưng bạn phải sử dụng mọi tham số từ 1..N để an toàn (vì các kiểu khác nhau cần' printf() 'et al để sử dụng các lượng khác nhau của Ví dụ, một 'double' cần 8 byte, trong đó' int' thường chỉ cần 4 byte. Vì vậy, nếu bạn có 'int' và' double' trên stack, 'printf()' có Điều này không ngăn chặn những kẻ tấn công sử dụng các cuộc tấn công chuỗi định dạng với các số bị thiếu trong các số 'n $', nhưng chúng không cồng kềnh miễn là nó hoạt động cho chúng. –

1

Tôi thực sự figured này ra một mình trong khi tìm kiếm một cái gì đó cho câu hỏi của tôi. Bạn có thể thêm một số tham số, theo sau là $ vào mã định dạng, sau %. Vì vậy, nó sẽ như thế này:

const char *fmtNoQuantity = "The price of %2$s is $%3$.2f each."; 

Tức là, chuỗi sẽ sử dụng tham số thứ 2 và phao sẽ sử dụng thông số thứ 3. Lưu ý, tuy nhiên, đây là tiện ích POSIX, không phải là tính năng chuẩn của C.

Một phương pháp tốt hơn có thể là xác định chức năng in tùy chỉnh. Một cái gì đó như thế này:


typedef enum {fmtDefault, fmtMultiLine, fmtCSV, fmtNoPrices, fmtNoQuantity} fmt_id; 

void print_record(fmt_id fmt, unsigned int qty, const char *item, float price) 
{ 
    switch (fmt) { 
    case fmtMultiLine: 
     printf("Qty: %3u\n", qty); 
     printf("Item: %s\n", item); 
     printf("Price per item: $%.2f\n\n", price); 
     break; 
    case fmtCSV: 
     printf("%u,%s,%.2f\n", qty, item, price); 
     break; 
    case fmtNoPrices: 
     printf("%u x %s\n", qty, item); 
     break; 
    case fmtNoQuantity: 
     printf("The price of %s is $%.2f each.\n", item, price); 
     break; 
    default: 
     printf("%u x %s ($%.2f each)\n", qty, item, price); 
     break; 
    } 
} 
+2

Một vấn đề với phần mở rộng này là tham chiếu một tham số mà không tham chiếu tất cả các tham số trước đó gây ra một hành vi không xác định vì hàm 'printf' không biết kích thước của các tham số này. Và không có định dạng "bỏ qua tham số này" mà tôi biết, do đó, điều này thậm chí không thể được sử dụng để giải quyết vấn đề đó ... – Medinoc

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