JSON और CSV
csv मॉड्यूल से कोट्स वाली टेबल पढ़ना और लिखना, newline="" की आवश्यकता, json से संरचित डेटा रखना, और कौन से प्रकार रास्ते में बदल जाते हैं।
आप कैसे सीखना चाहते हैं?
वह समस्या जिसे हम हल कर रहे हैं
अध्याय 23 से हम फ़ाइलें .split(",") से तोड़ रहे हैं, और यह काम भी कर रहा था। अब एक फ़ाइल, जिसमें एक फ़ील्ड के अंदर कॉमा है:
name,note,price
pen,blue ink,15.0
bag,"large, sturdy",850.0with open("items.csv", "r", encoding="utf-8") as fh:
for line in fh:
print(line.strip().split(","))['name', 'note', 'price']
['pen', 'blue ink', '15.0']
['bag', '"large', ' sturdy"', '850.0']आख़िरी रो में तीन फ़ील्ड की जगह चार आ गए, और कोटेशन के निशान डेटा का हिस्सा बन गए।
कोटेशन ग़लत नहीं है — यही CSV का नियम है, ठीक इसी हालत के लिए बना है। ग़लती हमारे पढ़ने वाले कोड की है। और यह नियम ख़ुद लिखने चलें तो सामने आएगा कोटेशन के अंदर कोटेशन, फ़ील्ड के अंदर नई लाइन — एक छोटा काम धीरे-धीरे बड़ा काम बनता जाता है।
यह काम पहले से किया हुआ है।
import csv
with open("items.csv", "r", encoding="utf-8", newline="") as fh:
for row in csv.reader(fh):
print(row)['name', 'note', 'price']
['pen', 'blue ink', '15.0']
['bag', 'large, sturdy', '850.0']यह अध्याय दो फ़ॉर्मैट के बारे में है — टेबल के लिए CSV, और ढाँचे वाले डेटा के लिए JSON — और दोनों के मॉड्यूल पायथन के साथ ही आते हैं।
इस अध्याय के अंत तक आप कर पाएंगे
csv.readerऔरcsv.DictReaderसे पढ़ना, औरcsv.writerसे लिखना- बताना कि
newline=""क्यों लिखा जाता है json.dumps/loadsऔरjson.dump/loadका फ़र्क़ बताना- बताना कि JSON से कौन-से पायथन टाइप ठीक-ठाक वापस आते हैं और कौन-से नहीं
- तय करना कि कब CSV और कब JSON
JSONDecodeErrorपढ़कर समस्या ढूँढना
पहले से क्या आना चाहिए: एक्सेप्शन।
CSV पढ़ना
csv.reader हर रो को एक लिस्ट के रूप में देता है। लेकिन पोज़िशन याद रखनी पड़ती है — row[2] कौन-सा था?
csv.DictReader पहली रो को हेडर मान लेता है और हर रो को डिक्शनरी बना देता है:
import csv
with open("items.csv", "r", encoding="utf-8", newline="") as fh:
for row in csv.DictReader(fh):
print(row["name"], "-", row["price"], type(row["price"]))pen - 15.0 <class 'str'>
bag - 850.0 <class 'str'>दो बातें।
नाम से पहुँचा जा सकता है, इसलिए कॉलम का क्रम बदल जाए तब भी कोड नहीं टूटता। लगभग हमेशा DictReader ही बेहतर चुनाव है।
लेकिन price अब भी एक स्ट्रिंग है। csv मॉड्यूल फ़ील्ड अलग करता है, कन्वर्ट नहीं करता — CSV फ़ाइल में कहीं नहीं लिखा होता कि कौन-सा कॉलम नंबर है। float() और int() आपको ही कॉल करने होंगे, ठीक अध्याय 23 की तरह।
CSV लिखना
import csv
rows = [["pen", "blue ink", 15.0], ["bag", "large, sturdy", 850.0]]
with open("out.csv", "w", encoding="utf-8", newline="") as fh:
writer = csv.writer(fh)
writer.writerow(["name", "note", "price"])
writer.writerows(rows)name,note,price
pen,blue ink,15.0
bag,"large, sturdy",850.0ध्यान दें कि large, sturdy अपने-आप कोटेशन के अंदर चला गया — और blue ink नहीं गया, क्योंकि उसकी ज़रूरत नहीं थी। जो हम ख़ुद पढ़ नहीं पाए, वह लिखना भी हमें नहीं पड़ा।
डिक्शनरी की लिस्ट हो तो DictWriter:
import csv
rows = [
{"name": "pen", "price": 15.0},
{"name": "bag", "price": 850.0},
]
with open("out.csv", "w", encoding="utf-8", newline="") as fh:
writer = csv.DictWriter(fh, fieldnames=["name", "price"])
writer.writeheader()
writer.writerows(rows)name,price
pen,15.0
bag,850.0fieldnames कॉलम का क्रम भी तय करता है, और writeheader() के बिना हेडर वाली रो नहीं आएगी।
newline=""क्यों?csvमॉड्यूल ख़ुद तय करता है कि रो के आख़िर में क्या लगेगा (\r\n)। फ़ाइल को आम तरीके से खोलें तो विंडोज़ पर पायथन लाइन-एंड का एक बार और अनुवाद करता है, जिससे हर रो के बाद एक फ़ालतू ख़ाली लाइन आ जाती है। लिनक्स और macOS पर यह दूसरा अनुवाद नहीं होता, इसलिए वहाँ फ़र्क़ दिखता नहीं।newline=""वह अनुवाद बंद करता है, इसलिए कोड हर जगह एक जैसा चलता है। पढ़ते समय भी लिखें — तब कोटेशन के अंदर की नई लाइन ठीक से पढ़ी जाती है।
JSON — जहाँ ढाँचा टेबल में नहीं समाता
CSV एक टेबल है: रो और कॉलम, सब सपाट। लेकिन अगर एक ऑर्डर के अंदर लाइनों की लिस्ट हो, और हर लाइन के अंदर टैग की लिस्ट? टेबल में वह नहीं समाता।
import json
order = {"name": "pen", "price": 15.0, "tags": ["ink", "blue"], "stocked": True, "note": None}
print(json.dumps(order)){"name": "pen", "price": 15.0, "tags": ["ink", "blue"], "stocked": true, "note": null}json.dumps एक पायथन ऑब्जेक्ट को स्ट्रिंग में बदलता है। आकार पायथन जैसा ही दिखता है, दो फ़र्क़ों के अलावा: True बन गया true, और None बन गया null। JSON एक अलग भाषा है, जो बहुत सी प्रोग्रामिंग भाषाओं के बीच साझा है — इसलिए उसकी अपनी स्पेलिंग है।
इंसान के पढ़ने लायक चाहिए तो:
import json
order = {"name": "pen", "tags": ["ink", "blue"]}
print(json.dumps(order, indent=2)){
"name": "pen",
"tags": [
"ink",
"blue"
]
}और वापस लाना:
import json
text = '{"name": "pen", "price": 15.0, "stocked": true, "note": null}'
order = json.loads(text)
print(order)
print(type(order["price"]), type(order["stocked"]), order["note"] is None){'name': 'pen', 'price': 15.0, 'stocked': True, 'note': None}
<class 'float'> <class 'bool'> TrueCSV से सबसे बड़ा फ़र्क़ यही है। price नंबर बनकर लौटा, stocked True बनकर लौटा — कोई कन्वर्ज़न कॉल नहीं करना पड़ा। JSON टाइप भी साथ लेकर चलता है; CSV नहीं।
चारों नाम याद रखना आसान है: dumps/loads स्ट्रिंग के साथ काम करते हैं, dump/load फ़ाइल के साथ। आख़िर का s मतलब string।
import json
from pathlib import Path
order = {"name": "pen", "price": 15.0}
Path("order.json").write_text(json.dumps(order, indent=2) + "\n", encoding="utf-8")
with open("order.json", "r", encoding="utf-8") as fh:
back = json.load(fh)
print(back){'name': 'pen', 'price': 15.0}JSON जो वापस नहीं देता
हर पायथन ऑब्जेक्ट JSON में नहीं जाता, और जो जाते हैं वे सब वैसे के वैसे वापस नहीं आते।
import json
data = {"pair": (1, 2), "numbers": {3, 4}}
print(json.dumps({"pair": data["pair"]}))
print(json.dumps(data)){"pair": [1, 2]}
TypeError: Object of type set is not JSON serializable(एरर से पहले पायथन traceback की कुछ लाइनें भी प्रिंट करता है; इस अध्याय में सिर्फ़ आख़िरी लाइन दिखाई गई है।)
टपल चुपचाप लिस्ट बन जाता है। JSON में टपल जैसा कुछ नहीं, इसलिए लिखते समय कोई शिकायत नहीं आती — लेकिन वापस लाएँ तो आपको लिस्ट मिलेगी। अध्याय 14 का "नहीं बदलेगा" वाला वादा रास्ते में खो जाता है।
सेट तो जाता ही नहीं, और यह अच्छा है — चुपचाप ग़लत होने से रुक जाना बेहतर। ज़रूरत हो तो sorted(...) करके लिस्ट बनाकर भेजें।
एक और, जो ज़्यादा काटता है:
import json
data = {1: "pen", 2: "bag"}
text = json.dumps(data)
print(text)
print(json.loads(text)){"1": "pen", "2": "bag"}
{'1': 'pen', '2': 'bag'}JSON में key हमेशा स्ट्रिंग होती है। नंबर वाली key दें तो वह चुपचाप स्ट्रिंग बन जाती है, और स्ट्रिंग बनकर ही लौटती है। इसलिए पहले data[1] चलता था, और वापस आने के बाद data["1"] चाहिए।
याद रखने की लिस्ट छोटी है: डिक्शनरी, लिस्ट, स्ट्रिंग, नंबर, True/False, None — ये सुरक्षित हैं। टपल बदल जाता है। सेट, तारीख़ और अपने बनाए ऑब्जेक्ट नहीं जाते।
JSON सख़्त है
import json
print(json.loads("{'name': 'pen'}"))json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)पायथन में सिंगल और डबल कोटेशन बराबर हैं। JSON में नहीं — वहाँ सिर्फ़ डबल कोटेशन चलता है, और आख़िरी चीज़ के बाद कॉमा नहीं रखा जा सकता।
मैसेज लाइन और कॉलम बता देता है, इसलिए बड़ी फ़ाइल में भी जगह मिल जाती है। और JSONDecodeError असल में एक ValueError है — इसलिए except ValueError से भी पकड़ा जा सकता है, जैसा पिछले अध्याय का परिवार वाला नियम कहता है।
कब कौन-सा
CSV लें जब डेटा एक सपाट टेबल हो और लोग उसे स्प्रेडशीट में खोलेंगे। रो ज़्यादा हों तब भी फ़ाइल छोटी रहती है।
JSON लें जब ढाँचा हो — अंदर लिस्ट, अंदर डिक्शनरी — या जब टाइप सही-सलामत वापस आने चाहिए। सेटिंग्स फ़ाइलें और वेब API लगभग हमेशा JSON होते हैं।
एक पूरा उदाहरण
items.csv:
name,note,price,quantity
pen,blue ink,15.0,3
bag,"large, sturdy",850.0,1
ink,,120.0,2
clip,broken,,4main.py:
"""Read a CSV of items, total it, and write the result as JSON."""
import csv
import json
from pathlib import Path
TAX_RATE = 0.15
def read_items(path):
"""Returns the usable rows and the complaints. csv handles the quoting."""
items = []
problems = []
with open(path, "r", encoding="utf-8", newline="") as fh:
for number, row in enumerate(csv.DictReader(fh), start=2):
try:
items.append({
"name": row["name"],
"note": row["note"],
"amount": round(
float(row["price"]) * int(row["quantity"]) * (1 + TAX_RATE), 2),
})
except ValueError as err:
problems.append(f"line {number}: {err}")
return items, problems
def main():
items, problems = read_items(Path("items.csv"))
summary = {
"items": items,
"total": round(sum(item["amount"] for item in items), 2),
"skipped": problems,
}
Path("summary.json").write_text(
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print(Path("summary.json").read_text(encoding="utf-8"), end="")
if __name__ == "__main__":
main(){
"items": [
{
"name": "pen",
"note": "blue ink",
"amount": 51.75
},
{
"name": "bag",
"note": "large, sturdy",
"amount": 977.5
},
{
"name": "ink",
"note": "",
"amount": 276.0
}
],
"total": 1305.25,
"skipped": [
"line 5: could not convert string to float: ''"
]
}चार चीज़ें देखने लायक हैं।
अंदर CSV आता है, बाहर JSON जाता है, और यह दोनों फ़ॉर्मैट के स्वभाव से ही आता है। इनपुट एक सपाट टेबल है, जिसे स्प्रेडशीट में भी खोला जा सकता है। आउटपुट सपाट नहीं है — अंदर चीज़ों की लिस्ट, उसके अंदर डिक्शनरी, और साथ में शिकायतों की एक और लिस्ट। यह CSV में नहीं लिखा जा सकता था।
large, sturdy पूरे रास्ते सही-सलामत रहा। csv ने उसे ठीक से पढ़ा, और json ने ठीक से लिखा। इस अध्याय की शुरुआत वाली समस्या यहाँ कहीं नहीं है।
ख़ाली note और ख़ाली price — दोनों ख़ाली स्ट्रिंग हैं, लेकिन नतीजा अलग। CSV में ख़ाली फ़ील्ड का मतलब "" है, None नहीं। note स्ट्रिंग के रूप में ही रहता है और कोई दिक्कत नहीं करता; लेकिन float("") एक ValueError उठाता है, और इसलिए clip वाली रो शिकायत के साथ छूट जाती है। CSV में "नहीं है" जैसा कुछ नहीं — सिर्फ़ "ख़ाली" है, और उसका मतलब क्या है, यह आपको ही तय करना होता है।
start=2 संयोग नहीं है। DictReader पहली रो को हेडर के रूप में खा जाता है, इसलिए उसकी दी हुई पहली डेटा-रो फ़ाइल की दूसरी लाइन है। जो मैसेज लोग फ़ाइल खोलकर मिलाएँगे, उसका नंबर फ़ाइल से मेल खाना चाहिए।
जब कुछ टूट जाए
CSV फ़ाइल में हर रो के बाद एक ख़ाली लाइन (विंडोज़ पर) newline="" नहीं दिया गया। लिखते समय दें।
KeyError: 'price' — जबकि कॉलम मौजूद है हेडर की स्पेलिंग या फ़ालतू स्पेस मेल नहीं खा रहा। print(reader.fieldnames) से देखें कि पायथन ने असल में क्या पढ़ा।
नंबर के साथ हिसाब करते समय TypeError csv सब कुछ स्ट्रिंग के रूप में देता है। float() या int() कॉल करना होगा।
पहली रो भी डेटा के रूप में आ रही है आप csv.reader इस्तेमाल कर रहे हैं, जो हेडर नहीं पहचानता। DictReader लें, या पहली रो अलग से हटाएँ।
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes सिंगल कोटेशन, या आख़िर में फ़ालतू कॉमा। JSON पायथन नहीं है।
TypeError: Object of type set is not JSON serializable सेट, तारीख़ या अपना बनाया कोई ऑब्जेक्ट भेजा गया है। पहले उसे लिस्ट या स्ट्रिंग में बदलें।
वापस लाने के बाद मेरे टपल लिस्ट बन गए यही होना है — JSON में टपल नहीं है। ज़रूरत हो तो वापस लाने के बाद ख़ुद tuple(...) करें।
पहले data[1] चल रहा था, वापस लाने के बाद KeyError JSON में key हमेशा स्ट्रिंग होती है। अब data["1"]।
चरण 4 / 6 — अनुमान
अपनी समझ की जाँच करें
तीन कॉलम वाली फ़ाइल, जिसे हाथ से तोड़ा जा रहा है। कितने फ़ील्ड मिलेंगे?
# items.csv
# name,note,price
# pen,blue ink,15.0
# bag,"large, sturdy",850.0
with open("items.csv", "r", encoding="utf-8") as fh:
for line in fh:
print(len(line.strip().split(",")))- A3 3 4
- B3 3 3
- C3 3 5
- Dएक `ValueError`
DictReader ने रो पढ़ ली है। दूसरी लाइन पर क्या होगा?
# items.csv
# name,price
# pen,15
import csv
with open("items.csv", "r", encoding="utf-8", newline="") as fh:
row = next(csv.DictReader(fh))
print(row)
print(row["price"] + 1)- Aडिक्शनरी प्रिंट होती है, फिर `16`
- Bडिक्शनरी प्रिंट होती है, फिर `151`
- Cडिक्शनरी प्रिंट होती है, फिर `TypeError` — `price` अब भी एक स्ट्रिंग है
- Dएक `KeyError`
json.dumps को एक पायथन डिक्शनरी मिली है। क्या प्रिंट होगा?
import json
print(json.dumps({"name": "pen", "stocked": True, "note": None}))- A{'name': 'pen', 'stocked': True, 'note': None}
- B{"name": "pen", "stocked": True, "note": None}
- C{"name": "pen", "stocked": true}
- D{"name": "pen", "stocked": true, "note": null}
उत्तर देने के लिए अकाउंट आवश्यक है
अपने उत्तर जाँचने के लिए साइन इन करें
प्रश्न ऊपर दिए गए हैं, और मन में उत्तर सोचना ही मुख्य कार्य है। सही उत्तर, व्याख्या और तीन-स्तरीय संकेत देखने के लिए साइन इन करें।
आपकी बारी
एक books.csv बनाएँ — title,author,pages,year कॉलम के साथ कम से कम छह रो। कम से कम एक title में कॉमा रखें (इसलिए कोटेशन के अंदर), एक रो में pages ख़ाली रखें, और एक में pages की जगह अक्षर रखें।
फिर library.py लिखें जो:
DictReaderसे फ़ाइल पढ़ेगा, और ख़राब रो के लिए लाइन नंबर के साथ शिकायतें जमा करेगा- लेखक के हिसाब से एक सारांश बनाएगा — किसकी कितनी किताबें, कुल कितने पेज
- पूरा
library.jsonमेंindent=2के साथ लिखेगा - फिर फ़ाइल को दोबारा पढ़कर पक्का करेगा कि कुल संख्या वही है
फिर पाँच प्रयोग:
DictReaderकी जगहline.split(",")इस्तेमाल करें। कॉमा वालेtitleका क्या होता है?- लिखते समय
newline=""हटा दें, फिर CSV फ़ाइल खोलकर देखें। विंडोज़ पर हर रो के बाद एक ख़ाली लाइन दिखेगी; लिनक्स और macOS पर कोई फ़र्क़ नहीं दिखेगा — फिर भी वहाँ भीnewline=""रखें, ताकि कोड हर जगह एक जैसा चले। - सारांश में एक सेट रखें (जैसे लेखकों का सेट) और JSON में लिखने की कोशिश करें। मैसेज पढ़ें, फिर ठीक करें।
library.jsonफ़ाइल हाथ से खोलकर एक डबल कोटेशन को सिंगल कोटेशन बना दें, फिर पढ़ने की कोशिश करें। क्या मैसेज लाइन और कॉलम बताता है?- साल को डिक्शनरी की key बनाएँ — जैसे
{2020: [...]}— फिर लिखकर दोबारा पढ़ें। अब किस key से ढूँढना पड़ रहा है?
आख़िरी दो प्रयोग इस अध्याय की मूल बात बताते हैं: फ़ाइल में लिखना मतलब अनुवाद करना, और हर अनुवाद में कुछ न कुछ बदलता है। क्या बदलता है यह पता हो तो फ़ॉर्मैट चुना जा सकता है; न पता हो तो फ़ॉर्मैट आपको चुन लेता है।
चरण 6 / 6
चुनौती — अध्याय की क्विज़
सरल से कठिन — दस प्रश्न, अंतिम वाले जानबूझकर चुनौतीपूर्ण बनाए गए हैं।
क्विज़ देने के लिए साइन इन करें