[0]zero-index clytie0x/zero-index

2.2 จัดความกว้างและตาราง

นาฬิกาดิจิทัลแสดง 07:05 ไม่ใช่ 7:5 ใบเสร็จร้านค้าเรียงราคาให้ตรงกันเป็นคอลัมน์ โจทย์หลายข้อสั่งรูปแบบแบบนี้มาตรงๆ เช่น "พิมพ์เวลาเป็นเลขสองหลัก" หรือ "พิมพ์ตารางตามตัวอย่าง" หน้านี้ใช้ตัวเลขที่ใส่ไว้หลัง % กำหนดว่าแต่ละช่องกว้างเท่าไร ชิดซ้ายหรือขวา และเติมอะไรลงในที่ว่าง

กำหนดความกว้างขั้นต่ำ

ตัวอย่างนี้ใส่ [ กับ ] ครอบไว้ จะได้มองเห็นว่าช่องว่างอยู่ตรงไหน

#include <stdio.h>

int main()
{
    int score = 42;
    printf("[%d]\n", score);
    printf("[%5d]\n", score);
    printf("[%-5d]\n", score);
    printf("[%05d]\n", score);
    printf("[%3d]\n", 123456);
    return 0;
}
[42]
[   42]
[42   ]
[00042]
[123456]
เขียนความหมาย42 ได้
%5dกว้างอย่างน้อย 5 ตัว ชิดขวา เติมช่องว่างด้านซ้าย[ 42]
%-5dกว้างอย่างน้อย 5 ตัว ชิดซ้าย เติมช่องว่างด้านขวา[42 ]
%05dกว้างอย่างน้อย 5 ตัว เติมเลข 0 ด้านซ้ายแทนช่องว่าง[00042]

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

เลข 0 นำหน้า

โปรแกรมแปลงนาทีเป็นชั่วโมงในหน้า 1.2 พิมพ์ 7 hours 5 minutes ถ้าอยากได้แบบนาฬิกาดิจิทัล ใช้ %02d คือกว้าง 2 ตัว เติม 0 ข้างหน้า

#include <stdio.h>

int main()
{
    int total;
    scanf("%d", &total);
    int hours = total / 60;
    int minutes = total % 60;
    printf("%02d:%02d\n", hours, minutes);
    return 0;
}
425
07:05

ค่าที่มีสองหลักอยู่แล้วอย่าง 45 ก็พิมพ์ 45 ตามปกติ เลข 0 เติมเฉพาะตอนที่หลักไม่พอ รูปแบบเดียวกันใช้กับวันที่ 05/03/2569 หรือรหัสนักเรียน %05d ที่ต้องยาว 5 หลักเสมอ

ใช้กับชนิดอื่น

ความกว้างใส่ได้กับทุกตัวกำหนด ส่วน %f ใส่ได้ทั้งความกว้างและจำนวนทศนิยมพร้อมกัน เช่น %8.2f คือกว้าง 8 ตัว ทศนิยม 2 ตำแหน่ง

#include <stdio.h>

int main()
{
    printf("[%8.2f]\n", 3.14159);
    printf("[%-8.2f]\n", 3.14159);
    printf("[%10s]\n", "Mali");
    printf("[%-10s]\n", "Mali");
    printf("[%3c]\n", 'A');
    printf("[%05d]\n", -42);
    return 0;
}
[    3.14]
[3.14    ]
[      Mali]
[Mali      ]
[  A]
[-0042]
  • ความกว้างของ %8.2f นับทุกตัวอักษร 3.14 ยาว 4 ตัวรวมจุด จึงเติมช่องว่างอีก 4 ช่อง
  • เครื่องหมายลบก็นับในความกว้าง -42 กับ %05d ได้ -0042 ยาว 5 ตัวพอดี

จัดตาราง

ตารางคะแนนที่แต่ละคอลัมน์ตรงกัน ทำได้ด้วยการให้ทุกบรรทัดใช้ความกว้างชุดเดียวกัน

#include <stdio.h>

int main()
{
    printf("%-10s%6s%6s%8s\n", "Name", "Math", "Sci", "Avg");
    printf("%-10s%6d%6d%8.2f\n", "Mali", 78, 91, (78 + 91) / 2.0);
    printf("%-10s%6d%6d%8.2f\n", "Somchai", 85, 62, (85 + 62) / 2.0);
    printf("%-10s%6d%6d%8.2f\n", "Kanokwan", 100, 97, (100 + 97) / 2.0);
    return 0;
}
Name        Math   Sci     Avg
Mali          78    91   84.50
Somchai       85    62   73.50
Kanokwan     100    97   98.50

วิธีคิด

  1. คอลัมน์ชื่อเป็นข้อความ คนอ่านชินกับข้อความชิดซ้าย จึงใช้ %-10s กว้างกว่าชื่อที่ยาวที่สุด (8 ตัว) เพื่อให้มีช่องว่างคั่นคอลัมน์ถัดไป
  2. คอลัมน์ตัวเลขชิดขวา หลักหน่วยจะตรงกัน เทียบค่ามากน้อยง่าย
  3. หัวตารางใช้ความกว้างเดียวกับข้อมูลในคอลัมน์นั้น "Math" ใช้ %6s คู่กับ %6d จึงชิดขวาตรงกับตัวเลข

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

ลองทำให้พัง

แบบที่ 1 ลืมเลข 0

ในโปรแกรม clock เขียน %2d แทน %02d

#include <stdio.h>

int main()
{
    int total;
    scanf("%d", &total);
    printf("%2d:%2d\n", total / 60, total % 60);
    return 0;
}
425
 7: 5

กว้าง 2 ตัวครบ แต่เติมด้วยช่องว่าง ไม่มีคำเตือนใดๆ บนจอมองผ่านๆ แทบไม่เห็นช่องว่างหน้าเลข 7 แต่ grader เห็น ข้อนี้ได้ผิด ถ้ามองด้วยตาไม่ออก ลองพิมพ์ [ ] ครอบแบบตัวอย่างแรกของหน้านี้ แล้วค่อยลบออกก่อนส่ง

แบบที่ 2 จัดตารางด้วย \t

ในหน้า 2.1 \t ทำให้คอลัมน์ตรงกันได้ ลองใช้กับตารางคะแนนแทนความกว้าง

#include <stdio.h>

int main()
{
    printf("Name\tMath\tSci\n");
    printf("Mali\t78\t91\n");
    printf("Somchai\t85\t62\n");
    printf("Kanokwan\t100\t97\n");
    return 0;
}
Name	Math	Sci
Mali	78	91
Somchai	85	62
Kanokwan	100	97

สามแถวแรกตรงกัน แต่ Kanokwan ยาว 8 ตัว ไปถึงจุดหยุด tab แรกพอดี \t จึงกระโดดไปจุดหยุดถัดไปแทน ทั้งแถวเลื่อนไปหนึ่งคอลัมน์ \t ใช้ได้ถ้ารู้แน่ว่าข้อความทุกตัวสั้นกว่าระยะ tab ถ้าไม่แน่ใจ ใช้ความกว้างแบบตัวอย่าง table ที่คุมได้ทุกตัวอักษร

อีกเรื่องคือโจทย์ที่ตัวอย่างผลลัพธ์ดูเหมือนเว้นหลายช่อง อาจเป็นช่องว่างหลายตัวหรือ tab ตัวเดียวก็ได้ ซึ่ง grader ถือว่าต่างกัน ถ้าโจทย์ไม่ได้บอกชัด ให้คัดลอกตัวอย่างผลลัพธ์มาดูว่าเป็นอะไร

แบบที่ 3 ลืมจุดใน %.2f

#include <stdio.h>

int main()
{
    double average = 81.666667;
    printf("%2f\n", average);
    return 0;
}
81.666667

ตั้งใจจะได้ 81.67 แต่ได้ทศนิยม 6 ตำแหน่ง และไม่มีคำเตือน เพราะ %2f ถูกไวยากรณ์ แปลว่า "กว้างอย่างน้อย 2 ตัว ทศนิยมตามปกติ" ตัวเลขหน้าจุดคือความกว้าง ตัวเลขหลังจุดคือจำนวนทศนิยม จุดตัวเดียวเปลี่ยนความหมายทั้งหมด

สรุป

  • %5d ชิดขวา %-5d ชิดซ้าย %05d เติม 0 ทั้งหมดคือกว้างอย่างน้อย 5 ตัว
  • ค่าที่ยาวกว่าความกว้างพิมพ์ครบ ไม่ถูกตัด
  • %02d ใช้กับเวลาและวันที่ %8.2f คือกว้าง 8 ทศนิยม 2
  • จัดตารางด้วยความกว้างชุดเดียวกันทุกแถว ข้อความชิดซ้าย ตัวเลขชิดขวา
  • \t เบี้ยวเมื่อข้อความยาวถึงระยะ tab ความกว้างคุมได้แน่นอนกว่า