15.2 cin และ cout
C++ รับข้อมูลด้วย cin และแสดงผลด้วย cout ข้อดีที่สุดคือไม่ต้องจำตัวกำหนดรูปแบบอีกแล้ว ไม่มี %d %lld %lf ไม่ต้องใส่ & หน้าตัวแปร ชนิดของตัวแปรเป็นตัวบอกเองว่าจะอ่านหรือพิมพ์แบบไหน ความผิดพลาดแบบลืมเปลี่ยน %d เป็น %lld ในบทที่ 1 จึงหายไปทั้งหมด หน้านี้สอนวิธีใช้ ส่วนเรื่องความเร็วอยู่ในหน้าถัดไป
cout พิมพ์ทุกชนิดด้วย <<
#include <bits/stdc++.h>
using namespace std;
int main()
{
int score = 85;
long long population = 3000000000;
double height = 1.75;
char grade = 'A';
cout << "score = " << score << '\n';
cout << "population = " << population << '\n';
cout << "height = " << height << '\n';
cout << "grade = " << grade << '\n';
return 0;
}
score = 85
population = 3000000000
height = 1.75
grade = A
<< ส่งค่าทางขวาไปที่ cout ทีละชิ้น เรียงจากซ้ายไปขวา หนึ่งบรรทัดจึงส่งได้หลายชิ้นต่อกัน เรียกว่าการต่อเป็นสาย (chain) แต่ละชิ้นจะเป็นข้อความ ตัวแปร หรือผลคำนวณก็ได้ เทียบกับ printf ที่เรียนมา
| ชนิด | printf | cout |
|---|---|---|
int | printf("%d\n", score); | cout << score << '\n'; |
long long | printf("%lld\n", population); | cout << population << '\n'; |
char | printf("%c\n", grade); | cout << grade << '\n'; |
| หลายค่า | printf("%d %d\n", a, b); | cout << a << ' ' << b << '\n'; |
แถวสุดท้ายสำคัญ cout ไม่เติมช่องว่างระหว่างชิ้นให้ ถ้าอยากได้ช่องว่างต้องส่ง ' ' เองทุกครั้ง ลืมแล้วตัวเลขจะติดกัน ดูได้ในส่วนลองทำให้พัง ส่วน double ไม่อยู่ในตาราง เพราะมีเรื่องต้องรู้มากกว่านั้น อยู่ในหัวข้อจัดทศนิยมด้วย fixed และ setprecision
cin รับข้อมูลด้วย >>
กลับไปที่ที่ดินกว้าง 50000 เมตร ยาว 50000 เมตรในหน้า 1.3 คราวนี้รับด้วย cin
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long width, length;
cin >> width >> length;
cout << "area = " << width * length << '\n';
return 0;
}
50000 50000
area = 2500000000
- ไม่มี
&และไม่มี%lldแค่ประกาศเป็นlong longแล้วcinก็อ่านเป็นlong longให้เอง cin >> width >> length;อ่านสองตัวเรียงกัน เหมือนscanf("%lld %lld", ...)- เหมือน
scanfตรงที่ข้ามช่องว่างและการขึ้นบรรทัดใหม่ระหว่างตัวเลขให้เอง ข้อมูลจะอยู่บรรทัดเดียวกันหรือคนละบรรทัดก็อ่านได้
cin ไม่ได้ช่วยเรื่อง overflow
cin แค่อ่านให้ตรงชนิด การเลือกชนิดยังเป็นหน้าที่ของน้อง ถ้าประกาศ width กับ length เป็น int ผลคูณก็ยังล้นแบบเดียวกับหน้า 1.3 ทุกอย่าง ต้องคิดขนาดจากขอบเขตในโจทย์เหมือนเดิม
จำทิศของลูกศรด้วยการนึกว่าข้อมูลไหลไปทางไหน cin >> x ข้อมูลไหลจากแป้นพิมพ์เข้าไปใน x ส่วน cout << x ค่าของ x ไหลออกไปที่หน้าจอ ถ้าเขียนลูกศรกลับด้าน คอมไพล์ไม่ผ่าน
cin อ่าน char ได้ด้วย และข้ามช่องว่างกับการขึ้นบรรทัดใหม่ข้างหน้าให้เอง เหมือน " %c" ในบทที่ 2 จึงไม่ต้องกลัวกับดักตัวอักษรค้างแบบ scanf("%c")
#include <bits/stdc++.h>
using namespace std;
int main()
{
int room;
char grade;
cin >> room >> grade;
cout << "room " << room << " grade " << grade << '\n';
return 0;
}
4
B
room 4 grade B
endl กับ '\n'
ขึ้นบรรทัดใหม่ใน cout ทำได้สองแบบ
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout << "line 1" << endl;
cout << "line 2" << '\n';
cout << "line 3\n";
return 0;
}
line 1
line 2
line 3
ผลเหมือนกันทั้งสามแบบ '\n' ในเครื่องหมายคำพูดเดี่ยวคือ char หนึ่งตัว ส่วนบรรทัดที่ 8 เอา \n ไปไว้ท้ายข้อความเลย ทั้งสองแบบเป็นตัวขึ้นบรรทัดใหม่ตัวเดียวกับที่ใช้ใน printf แต่ endl ทำมากกว่านั้นหนึ่งอย่าง
cout ไม่ได้ส่งตัวอักษรออกไปทีละตัว มันเก็บไว้ในที่พักข้อมูลชั่วคราวที่เรียกว่า buffer ก่อน พอสะสมได้เยอะแล้วค่อยส่งออกไปทีเดียวทั้งก้อน เหมือนรถขนของที่รอให้ของเต็มคันก่อนค่อยออกรถ ไม่ใช่วิ่งไปส่งทีละชิ้น endl คือ '\n' บวกคำสั่ง flush ที่บังคับให้ส่งของใน buffer ออกไปทันทีแม้จะยังไม่เต็ม
พิมพ์ไม่กี่บรรทัดไม่เห็นความต่าง แต่ถ้าอยู่ในลูปที่พิมพ์เป็นล้านบรรทัด endl ทำให้ออกรถเป็นล้านเที่ยว หน้า 15.3 จะวัดให้ดูว่าช้าลงหลายเท่า จึงมีกฎง่ายๆ ว่า ขึ้นบรรทัดใหม่ด้วย '\n' เสมอ ไม่ต้องห่วงว่าข้อมูลจะไม่ออก ตอนโปรแกรมจบ ของที่ค้างใน buffer จะถูกส่งออกให้ครบเอง
อ่านจนหมดข้อมูล
ในบทที่ 4 อ่านจนหมดด้วย while (scanf("%d", &x) == 1) แบบ cin เขียนสั้นกว่า
#include <bits/stdc++.h>
using namespace std;
int main()
{
int score;
int count = 0;
long long total = 0;
while (cin >> score) {
count++;
total += score;
}
cout << count << " scores, total " << total << '\n';
return 0;
}
72 85 64
90 58
5 scores, total 369
cin >> score ใช้เป็นเงื่อนไขได้ อ่านสำเร็จเป็นจริง อ่านไม่ได้เพราะข้อมูลหมดหรือเจอตัวที่ไม่ใช่ตัวเลขเป็นเท็จ ทดลองในหน้าต่างดำก็จบข้อมูลด้วย Enter Ctrl+Z Enter เหมือนเดิม
สังเกตว่าตัวแปรชื่อ count อยู่ใน main จึงไม่ชนกับ count ใน std แบบที่ 2 ในหน้า 15.1 ชนเพราะประกาศไว้นอก main
จัดทศนิยมด้วย fixed และ setprecision
cout พิมพ์ double ไม่เหมือน printf("%f") ลองดูก่อน
#include <bits/stdc++.h>
using namespace std;
int main()
{
double average = 245 / 3.0;
cout << average << '\n';
cout << 1234567 / 3.0 << '\n';
cout << 98765432.0 << '\n';
cout << 2.0 << '\n';
return 0;
}
81.6667
411522
9.87654e+07
2
ถ้าไม่บอกอะไร cout จะพิมพ์ให้สั้นที่สุดโดยใช้ตัวเลขรวมไม่เกิน 6 หลัก (นับทุกหลักที่มีความหมาย ไม่ใช่นับแค่หลังจุด) 81.666666... จึงเหลือ 81.6667 ตัวเลขที่ใหญ่หน่อยอย่าง 411522.333... หลักหลังจุดหายหมด ตัวที่ใหญ่กว่านั้นอีกจะกลายเป็นรูปแบบวิทยาศาสตร์ 9.87654e+07 ซึ่งแปลว่า 9.87654 × 107 และ 2.0 ก็พิมพ์แค่ 2 ไม่มีจุดเลย ไม่มีแบบไหนตรงกับที่โจทย์สั่งว่า "ทศนิยม 2 ตำแหน่ง"
วิธีบอกให้พิมพ์แบบที่ต้องการคือส่งตัวจัดรูปแบบสองตัวเข้าไปใน cout
#include <bits/stdc++.h>
using namespace std;
int main()
{
double average = 245 / 3.0;
cout << fixed << setprecision(2); // (1)!
cout << average << '\n';
cout << 1234567 / 3.0 << '\n';
cout << 98765432.0 << '\n';
cout << 2.0 << '\n';
cout << 17 << '\n'; // (2)!
return 0;
}
fixedแปลว่าใช้จำนวนตำแหน่งทศนิยมตายตัว และsetprecision(2)บอกว่าตายตัวที่ 2 ตำแหน่ง สองตัวนี้รวมกันได้ผลแบบเดียวกับ%.2fส่งเข้าไปครั้งเดียวแล้วมีผลกับdoubleทุกตัวที่พิมพ์หลังจากนั้น ไม่ต้องใส่ซ้ำทุกบรรทัดsetprecisionอยู่ในไฟล์iomanipซึ่งbits/stdc++.hดึงมาให้แล้ว- จำนวนเต็มไม่โดนผลกระทบ
17ยังพิมพ์เป็น17ไม่ใช่17.00
81.67
411522.33
98765432.00
2.00
17
โปรแกรมที่ต้องตอบเป็นทศนิยมให้ใส่ cout << fixed << setprecision(N); ไว้ต้น main ครั้งเดียว แล้วพิมพ์ตามปกติ
เลขที่ตกตรงครึ่งพอดี
เรื่องปัดเศษตรงครึ่งในหน้า 2.1 ยังต้องระวังเหมือนเดิม บนเครื่องที่เขียนเอกสารนี้ cout พิมพ์ 0.125 แบบสองตำแหน่งได้ 0.12 และ 0.25 แบบหนึ่งตำแหน่งได้ 0.2 ซึ่งต่างจาก printf ในโปรแกรมภาษา C บนเครื่องเดียวกันที่ได้ 0.13 กับ 0.3 ไม่ต้องจำว่าแบบไหนได้อะไร จำแค่ว่าเลขตรงครึ่งพอดีเชื่อไม่ได้ คนตั้งโจทย์จึงหลีกเลี่ยงอยู่แล้ว
ลองทำให้พัง
แบบที่ 1 ลูกศรกลับด้าน
ในโปรแกรมที่รับคะแนนหนึ่งตัวแล้วพิมพ์ออกมา เขียน cin << score; แทน cin >> score;
shift.cpp:7:9: error: no match for 'operator<<' (operand types are 'std::istream' {aka 'std::basic_istream<char>'} and 'int')
7 | cin << score;
| ~~~ ^~ ~~~~~
| | |
| | int
| std::istream {aka std::basic_istream<char>}
ต่อจากนี้ยังมี note อีกกว่า 450 บรรทัด ไล่ทุก << ที่ compiler รู้จัก ไม่ต้องอ่าน ข้อความ error ของ C++ ยาวแบบนี้บ่อย ให้อ่านแค่บรรทัดแรกที่มีคำว่า error ซึ่งบอกว่าไม่มี << ที่ใช้กับ std::istream (ชนิดของ cin) และ int ได้ แปลง่ายๆ ว่า cin ใช้ << ไม่ได้ เปลี่ยนเป็น >> ก็หาย ถ้าใช้ cout >> ก็ได้ error ยาวแบบเดียวกัน
แบบที่ 2 ลืมช่องว่างระหว่างตัวเลข
#include <bits/stdc++.h>
using namespace std;
int main()
{
int hours = 2, minutes = 15;
cout << hours << minutes << '\n';
cout << hours << " " << minutes << '\n';
return 0;
}
215
2 15
บรรทัดแรกได้ 215 ซึ่งดูเหมือนตัวเลขตัวเดียว คอมไพล์ผ่านไม่มีคำเตือนเพราะ compiler ไม่รู้ว่าเราตั้งใจให้มีช่องว่าง ส่งแบบนี้ไป grader ตรวจว่าผิดทันที คั่นด้วย " " หรือ ' ' ได้ผลเหมือนกัน
แบบที่ 3 ใช้ setprecision โดยไม่มี fixed
#include <bits/stdc++.h>
using namespace std;
int main()
{
double average = 245 / 3.0;
double distance = 1234.5;
cout << setprecision(2);
cout << average << '\n';
cout << distance << '\n';
return 0;
}
82
1.2e+03
ตั้งใจจะได้ 81.67 กับ 1234.50 แต่ได้ 82 กับ 1.2e+03 เพราะถ้าไม่มี fixed ตัวเลขใน setprecision หมายถึงจำนวนหลักทั้งหมด ไม่ใช่จำนวนตำแหน่งหลังจุด 81.67 เหลือสองหลักจึงปัดเป็น 82 ส่วน 1234.5 เหลือสองหลักได้ 1200 ซึ่งพิมพ์เป็น 1.2e+03 ใช้ fixed คู่กับ setprecision เสมอ
สรุป
cout << a << ' ' << b << '\n';พิมพ์ได้ทุกชนิด ไม่ต้องใช้%dแต่ต้องใส่ช่องว่างเองcin >> a >> b;อ่านตามชนิดของตัวแปร ไม่ต้องมี&ชนิดยังต้องเลือกให้ใหญ่พอเอง- ลูกศรชี้ทางที่ข้อมูลไหล
cin >>เข้าตัวแปรcout <<ออกจอ endlคือ'\n'บวก flush ใช้'\n'เสมอwhile (cin >> x)อ่านจนหมดข้อมูล- ทศนิยม N ตำแหน่ง ใส่
cout << fixed << setprecision(N);ครั้งเดียวต้นmainห้ามลืมfixed