Swift में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड
आपके पास कुछ ऐसा है जिसको सफ़र करना है, और राह सिर्फ़-टेक्स्ट है: एक JSON API जो राव बाइट्स को ठुकराता है, एक ईमेल चैनल जो अपने 7-बिट के जन्म को याद रखता है, एक URL जो उसी चीज़ पर घूट जाता है जिसे वह नाम न दे पाए, और एक कॉन्फ़िग फ़ाइल जो बस सबसे सादे चरों को ही मानती है। base64 के पैकिंग वाले पक्ष पर स्वागत है, जहाँ Swift एक मेटॉड कॉल में आपकी बाइट्स को अक्षरों की एक मुस्कुराहट वाली दीवार बना देता है, हर तीन बाइट्स पर करीब एक अतिरिक्त चर का चार्ज लेता है, और कुछ रैपिंग ऑप्शन्स भी मौजूद हैं क्योंकि दो अलग-अलग दशकों को लाइन लंबाई के बारे में राय थीं।
इस साइट का होम पेज फ़ॉर्मेट की बात पहले से ही विस्तार से समझा चुका है (64 प्रिंट करने योग्य चर, हर तीन इनपुट बाइट्स पर चार, और आख़िरी ग्रुप पर ज़्यादा से ज़्यादा दो = चरों की पैडिंग), इसलिए फ़ॉर्मेट की वह क्लास शुरू होने से पहले ही ख़त्म है। इस आर्टिकल के लिए जेब में ले जाने वाली दो बातें: base64 पैकिंग है, लॉकिंग नहीं, और यह पैकिंग आपके डेटा को करीब 33 प्रतिशत बढ़ा देती है, जो हर उस बार मायने रखती है जब आप किसी साइज़ लिमिट के पास हों। Swift में पूरा काम एक ही टाइप, Data, और एक टोटल मेटॉड, base64EncodedString(options:), से चला जाता है। ज़रूरी हुनर बस इतना है कि उस मेटॉड के चारों ओर के दो स्टेप्स में क्या होता है, यह जान लेना, क्योंकि मेटॉड खुद कभी फेल नहीं होता। फेल स्टेप्स होते हैं।
एक मेटॉड, ज़ीरो बहाने
Swift में base64 से जुड़ा सब कुछ Foundation फ्रामवर्क के Data पर रहता है, और भाषा की सबसे पहली रिलीज़ से वहीं रहता आ रहा है (Apple इस मेटॉड को iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0 और visionOS 1.0 से सूचीबद्ध करता है)। पाइपलाइन हमेशा वही तीन कदम होते हैं: अपना कंटेंट Data में डालो, मेटॉड कॉल करो, और स्ट्रिंग भेजो।
import Foundation
let note = "Pack it, wrap it, ship it."
let packed = Data(note.utf8).base64EncodedString()
print(packed) // UGFjayBpdCwgd3JhcCBpdCwgc2hpcCBpdC4=
उन तीन लाइन में दो बातें अच्छी नज़र डालने लायक हैं। पहली: Data(note.utf8) वह शांत कदम है: utf8 व्यू की परिभाषा के हिसाब से हर Unicode स्केलर का प्रतिनिधित्व कर सकता है, इसलिए यह कभी फेल नहीं होता, और यही वजह है कि अधिकांश उदाहरणों में यह डिफ़ॉल्ट है। फ़ेलेबल जुड़वाँ, note.data(using:), कुछ एनकोडिंग्स के लिए nil जवाब दे सकता है, और देता भी है, और वह पूरा "कौन-से बाइट्स" फैसला नीचे अपना अलग सेक्शन पाता है, क्योंकि वही वह पहली जगह है जहाँ आपका डेटा गायब हो सकता है। दूसरी: मेटॉड खुद टोटल है: यह हमेशा जवाब देता है, इसका कोई एरर केस नहीं है, और यह आपसे बस इतना ही पूछता है कि आप कौन-सी लाइन रैपिंग चाहते हैं। एक जुड़वाँ और भी है, base64EncodedData(options:), जो पैक किया हुआ नतीजा स्ट्रिंग की बजाय ASCII बाइट्स के Data के रूप में लौटाता है, उन पाइपलाइन्स के लिए जहाँ अगला ठहराव बायनेरी API है, टेक्स्ट फ़ील्ड नहीं।
और क्योंकि आपमें से आधे "मेरे Swift ऐप को base64 डिपेंडेंसी चाहिए" के रास्ते आए हैं: इंस्टॉल करने को कुछ नहीं है। Base64 Foundation का हिस्सा है, Foundation टूलचेन का हिस्सा है, और टूलचेन हर प्लेटफ़ॉर्म पर उसी ढंग से आता है। macOS पर वह Xcode या कमांड लाइन टूल्स है; Linux और Windows पर वह swift.org का इंस्टॉलर है, जहाँ इस लेख के लिखे जाने पर मौजूदा स्टेबल line 6.3.x है और Swiftly वर्ज़न मैनेजर अनुशंसित मुख्य द्वार है; और आधिकारिक Docker images कंटेनर वाली दुनिया को कवर करती हैं। आपका Package.swift खाली रहता है, और ऐसा ही रहना चाहिए।
पहला असली फैसला: कौन-से बाइट्स?
एक भी base64 चर बनने से पहले, आपने वह फैसला कर लिया है जो सबसे ज़्यादा मायने रखता है, क्योंकि base64 बाइट्स को पैक करता है, और स्ट्रिंग बस तब तक स्ट्रिंग है जब तक कि आप उसका बाइट फ़ॉर्म चुन न लें। UTF-8 सही-समझ वाला डिफ़ॉल्ट है और लगभग हर चीज़ के लिए सही जवाब, पर जिस पल आपका डेटा किसी लीगसी सिस्टम, बायनेरी प्रोटोकॉल, या Unicode के किसी कोने से आता है, वह चुनाव गायब होकर नहीं रहता:
import Foundation
let phrase = "héllo"
print(phrase.data(using: .utf8)?.count ?? -1) // 6
print(phrase.data(using: .ascii) == nil) // true
print(phrase.data(using: .utf16)?.count ?? -1) // 12
print(phrase.data(using: .utf16LittleEndian)?.count ?? -1) // 10
print(phrase.data(using: .utf8)!.base64EncodedString())
// aMOpbGxv
print(phrase.data(using: .utf16LittleEndian)!.base64EncodedString())
// aADpAGwAbABvAA==
| कन्वर्ज़न | "héllo" के बाइट्स | base64 क्या लेकर जाता है |
|---|---|---|
.utf8 |
6 | aMOpbGxv, वह स्पेलिंग जिसकी मॉडर्न APIs उम्मीद रखती हैं |
.ascii |
nil के साथ फेल |
एक्सेंट वाला चर 0x7F से ऊपर बैठता है और ASCII उसे ठुकरा देता है |
.utf16 |
12 | UTF-8 से दोगुना साइज़, और साथ में सामने सफ़र करने वाला दो-बाइट का बाइट-ऑर्डर मार्कर |
.utf16LittleEndian |
10 | BOM टैग के बिना वही शब्द: 10 बाइट्स, फिर भी यहाँ सूचीबद्ध BOM-मुक्त ऑप्शन्स में सबसे भारी |
उस आउटपुट में तीन सीखें छिपी हैं। data(using:) रूप फ़ेलेबल है और .ascii फेल होने का सबसे बड़ा उम्मीदवार है, इसलिए इसे फोर्स-अनराप करना वही रास्ता है जिससे एक बिल्कुल सही वाक्य क्रैश हुए ऐप बन जाता है। सादा .utf16 कन्वर्ज़न सामने एक दो-बाइट का बाइट-ऑर्डर मार्कर जोड़ देता है (लिटिल-एंडियन मशीन पर FF FE), और वह BOM आपके पैक्ड आउटपुट में सफ़र करता है और हर उस डिकोडर को उलझा देता है जिसने उसकी उम्मीद नहीं की थी। और साइज़ का गणित माफ़ी नहीं देता: एक लापरवाह चरसेट चुनाव आपको दोगुने डेटा पर base64 का पूरा चार्ज झेलने पर मजबूर करता है, इसलिए सवाल कभी "क्या यह एन्कोड होगा?" नहीं, बल्कि "दूसरा सिरा, जब वह अनपैक करे, उसे क्या मिलने की उम्मीद है?" होता है। स्वर्णिम नियम: सफ़र के दोनों सिरों को base64 शुरू होने से पहले बाइट फ़ॉर्म पर सहमत हो जाना चाहिए, क्योंकि डिकोडर के पास यह अंदाज़ा लगाने का कोई तरीका नहीं है कि आपने क्या चुना, और वह पूछने वाला भी नहीं है।
रैपिंग: दो आदतें, एक पैरामीटर
मेटॉड के ऑप्शन्स सब लाइन ब्रेकिंग के बारे में हैं, और सब इसलिए मौजूद हैं क्योंकि बीसवीं सदी के दो फ़ॉर्मेट इसपर सहमत नहीं हो पाए कि अक्षरों की लाइन कितनी लंबी होनी चाहिए। MIME, 1996 का ईमेल स्टैंडर्ड, base64 को CRLF लाइन एंडिंग्स के साथ 76 चरों पर रैप करता है। PEM, 1987 की Privacy-Enhanced Mail परंपरा, 64 चरों पर रैप करता है, और वही वह रूप है जो सर्टिफिकेट्स और कीज़ के अंदर मिलता है, यानी वह -----BEGIN CERTIFICATE----- ब्लॉक्स जो आपकी सर्वर्स की कॉन्फ़िग डायरेक्टरी में रखे रहते हैं।
import Foundation
let certBytes = Data((0..<300).map { UInt8($0 % 256) })
let raw = certBytes.base64EncodedString()
let pemStyle = certBytes.base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed])
let mimeStyle = certBytes.base64EncodedString(options: [.lineLength76Characters,
.endLineWithCarriageReturn, .endLineWithLineFeed])
print(raw.count) // 400 चर, एक ही लाइन पर
print(pemStyle.components(separatedBy: "\n").count) // 7 लाइनें, हर एक 64 से ज़्यादा नहीं
print(mimeStyle.components(separatedBy: "\r\n").count) // 6 लाइनें, हर एक 76 से ज़्यादा नहीं
| ऑप्शन | काम | सावधानी |
|---|---|---|
.lineLength64Characters |
64 चरों के बाद लाइन काटो, PEM की आदत | लाइन एंडिंग CRLF ही रहती है, जब तक आप कुछ और न कहें |
.lineLength76Characters |
76 चरों के बाद लाइन काटो, MIME की आदत | वही CRLF डिफ़ॉल्ट |
.endLineWithCarriageReturn |
लाइन एंडिंग में कैरिज रीटर्न शामिल करो | अकेला यह बस CR है, पुराने Mac की शक्ल में, और ज़्यादातर वक़्त आप इसे नहीं चाहते |
.endLineWithLineFeed |
लाइन एंडिंग में लाइन फ़ीड शामिल करो | CRLF का मतलब हो तो दोनों ऑप्शन्स पास कीजिए |
अब वह डिफ़ॉल्ट जिससे लोग हैरान होते हैं: किसी भी .lineLength ऑप्शन के लिए बग़ैर लाइन एंडिंग चुने, और जो लाइन एंडिंग आपको मिलती है वह CRLF है, पूरा कैरिज-रीटर्न-प्लस-लाइन-फ़ीड जोड़ा। मेटॉड की एक हाउस स्टाइल है, और उसकी हाउस स्टाइल 1996 की है। बस LF चाहिए? .endLineWithLineFeed के साथ एक्सप्लिसिट रूप से भुगतान कीजिए, और कुछ नहीं। एक और हाउस रूल दर्ज रखिए: आख़िरी लाइन को कभी ट्रेलिंग लाइन एंडिंग नहीं मिलती। रैप किया हुआ नतीजा हमेशा अपने आख़िरी डेटा चर या अपने = पैड्स पर ख़त्म होता है, चाहे आपने कोई भी ऑप्शन्स चुने हों, इसलिए आप जोड़कर पेस्ट कर सकते हैं बिना उस अकेली-छोटी खाली पंक्ति के जो अंत में लटक जाती। और बिल्कुल बिना ऑप्शन्स के, आउटपुट एक टूटी-फूटी नहीं, एक निरंतर लाइन होता है, जो JSON बॉडीज़, URLs, और API पेलोड्स के लिए सही रूप है: वही काम जो एक मॉडर्न Swift ऐप असल में ज़्यादातर वक़्त करता है।
Base64url: सफ़र कर पाने वाली स्ट्रिंग
स्टैंडर्ड वर्णमाला JSON का अच्छा नागरिक है और URL का बिल्कुल ख़राब नागरिक। क्वेरी स्ट्रिंग में + को फ़ॉर्म पार्सिंग स्पेस की तरह पढ़ता है, / पथ-सैपरेटर है, और = कीज़ को वैल्यूज़ से अलग करता है, इसलिए स्टैंडर्ड वर्णमाला को पर्सेंट-एन्कोड करने से वह छोटी होने की बजाय लंबी और बदसूरत हो जाती है। RFC 4648 का सेक्शन 5 बस इसी को ठीक करने के लिए मौजूद है: "URL और फ़ाइल-नाम सेफ़ वर्णमाला", जहाँ + बनकर - हो जाता है, / बनकर _, और = पैडिंग आमतौर पर गिरा दी जाती है, क्योंकि URL में पैड आमतौर पर %3D बन जाता है, और मक़सद ही बेकार हो जाता है। RFC एक ऐसी चेतावनी जोड़ता है जिसे फ्रेम करने लायक है: यह एन्कोडिंग "base64 encoding के बराबर नहीं समझी जानी चाहिए"। YouTube वीडियो आईडीज़, JWTs, और अधिकांश मॉडर्न API आइडेंटिफायर्स इसे बोलते हैं, इसलिए इस्तेमाल की उम्मीद रखिए।
import Foundation
extension Data {
var base64URLEncoded: String {
base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
let tricky = Data("The + / and = trio goes home.".utf8)
print(tricky.base64EncodedString())
// VGhlICsgLyBhbmQgPSB0cmlvIGdvZXMgaG9tZS4=
print(tricky.base64URLEncoded)
// VGhlICsgLyBhbmQgPSB0cmlvIGdvZXMgaG9tZS4
उस आउटपुट पर ध्यान से नज़र डालिए: यह ख़ास पेलोड बस इतना सा कि + या / नहीं बना, इसलिए दोनों स्पेलिंग्स के बस गिराई गई पैडिंग में फ़र्क़ है। एक बाइट बदलो और वे वर्णमाला में अलग-अलग हो जाएंगी, और यही तो पूरा मक़सद है। दो लड़ाई के नियम। डायलेक्ट एक बार चुनिए, उस बाउंडरी पर जहाँ आपका डेटा बाहरी दुनिया से मिलता है, और एक ही डॉक्यूमेंट के अंदर वर्णमालाएँ कभी मिक्स न कीजिए: स्टैंडर्ड डीकादीर को base64url मिल जाए (या इसके उल्टा), तो या तो वह इनपुट रिजेक्ट कर देगा, या लेनिएंट मोड्स में बाहरी चर हटाकर ग़लत बाइट्स हाथ में दे देगा। और अपने हेलपर का नाम ईमानदारी से रखिए, ताकि अगले डेवलपर को पता हो कि स्ट्रिंग base64url है, टाइपो नहीं। नई टूलचेन्स पर वही एक्सटेंशन छोटा हो सकता है: नवीनतम SDK बेटा अब नेटिव .base64URLAlphabet ऑप्शन के साथ आते हैं जो फ्रामवर्क के अंदर वर्णमाला की बदली करता है, साथ में मिलती-जुलता .omitPaddingCharacter ऑप्शन, और ओपन-सोर्स Foundation भी वही ऑप्शन्स बाद की टूलचेन्स के लिए उपलब्धता मार्कर के पीछे रखता है। जब तक वे आपके मिनिमम डेप्लॉयमेंट टारगेट तक न पहुँचें, चार-लाइन वाला एक्सटेंशन ही पोर्टेबल जवाब है, और संरचना के बल पर वह हर प्लेटफ़ॉर्म पर काम करता रहेगा।
JSON और APIs: वह Base64 जिसे आपने कभी माँगा नहीं
यह बात Codable के साथ काम करने वालों में सबसे ज़्यादा हैरान करती है, इसलिए यह अपना अलग सेक्शन कमाती है: JSONEncoder की Data प्रॉपर्टी के लिए डिफ़ॉल्ट स्ट्रैटेजी पहले से ही base64 है। अगर किसी Codable स्ट्रक्ट में Data फ़ील्ड है, तो एन्कोडर उसे स्टैंडर्ड base64 से अपने-आप पैक कर देता है, और लौटने पर JSONDecoder उसे अपने-आप अनपैक कर देता है। कोई ऑप्शन नहीं, कोई कन्फ़िगरेशन नहीं, कोई शोष-शौरी नहीं।
import Foundation
struct Snapshot: Codable {
let name: String
let icon: Data
}
let snap = Snapshot(name: "cat", icon: Data("🐱".utf8))
let json = try JSONEncoder().encode(snap)
print(String(decoding: json, as: UTF8.self))
// icon वायर पर "8J+QsQ==" के रूप में गुज़रा
icon प्रॉपर्टी वायर पर 8J+QsQ== के रूप में गुज़री क्योंकि वही हाउस स्टाइल है। विकल्प हैं, और जो दो आप असल में मिलेंगे वे हैं .custom, जो आपको डेटा और एक एन्कोडर सौंपकर रिप्रेज़ेंटेशन का फैसला करने देता है, और नया .deferredToData, जो डेटा इंस्टेंस खुद के हाथ में सौंपकर छोड़ देता है। जिस पल API को स्टैंडर्ड की बजाय base64url चाहिए, .custom वही जगह है जहाँ पिछले सेक्शन का आपका एक्सटेंशन लग जाता है:
import Foundation
extension Data {
var base64URLEncoded: String {
base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
struct Snapshot: Codable {
let name: String
let icon: Data
}
let encoder = JSONEncoder()
encoder.dataEncodingStrategy = .custom { data, enc in
var container = enc.singleValueContainer()
try container.encode(data.base64URLEncoded)
}
let json = try encoder.encode(Snapshot(name: "cat", icon: Data("🐱".utf8)))
print(String(decoding: json, as: UTF8.self))
// icon वायर पर "8J-QsQ" के रूप में गुज़रा
एक ऐसी चेतावनी जो चलता हुआ फ़ीचर प्रोडक्शन इंसीडेंट से अलग करती है: JSON स्ट्रिंग में राव लाइन ब्रेक नहीं हो सकता। अगर आप .lineLength ऑप्शन से पेलोड रैप करके उसे, इस्केपिंग किए बग़ैर, JSON डॉक्यूमेंट के अंदर इंटरपोलेट करें, तो आपने JSON वैल्यू ही नहीं बनाई; आपने base64 लहज़े वाला सिंटैक्स एरर बनाया है, और पार्सर इसे साबित कर देगा। रैप किया हुआ आउटपुट की जगह ईमेल बॉडीज़ और सर्टिफिकेट फ़ाइलों में है। जो भी JSON, URLs, या क्वेरी स्ट्रिंग्स के अंदर रहता है, उसे सादा, बिना रैप की गई स्ट्रिंग मिलती है।
Data URIs: स्ट्रिंग में बसी इमेज
वेब की पसंदीदा चाल फ़ाइल की बाइट्स को सीधे URL के अंदर जमा देना है: data:{mime};base64,{payload}। Swift में ऐसे एक को बनाना है पढ़ो, एन्कोड करो, और स्ट्रिंग जोड़ो:
import Foundation
let gif = Data(base64Encoded: "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")!
let uri = "data:image/gif;base64," + gif.base64EncodedString()
print(uri.hasPrefix("data:image/gif;base64,R0lGODlh")) // true
print(gif.count) // 42
इस उदाहरण में उसी मशहूर 42-बाइट की ट्रांसपेरेंट GIF (छोटी non-ट्रांसपेरेंट GIFs भी हैं, पर सभी जो जमा करते हैं, वह बस यही है) को दोबारा बनाकर एक डेटा URI में डाला गया है, जिसे ब्राउज़र दूसरे रिक्वेस्ट के बिना रेंडर कर देगा। Apple प्लेटफ़ॉर्म्स पर उल्टी दिशा एक वन-लाइनर है: वही Data जिसे आपने पैक किया, सीधा UIImage(data:) या NSImage(data:) में चला जाता है। ट्रेड-ऑफ साइज़ है, और वह चक्कर-दरा-चक्कर जुड़ता जाता है: 100-किलोबाइट की इमेज data:image/png;base64, प्रिफ़िक्स जोड़ने से पहले ही 133,000 चरों की स्ट्रिंग बन जाती है। डेटा URIs आइकॉन्स, एवेटार, और छोटे-मोटे एसेट्स के लिए चमकते हैं, और हिरो फ़ोटोज़ के लिए बबल-बबल करके बैंडविड्थ फूल देते हैं, इसलिए उन्हें छोटी-छोटी चीज़ों के लिए रखिए।
JWTs: पहले दो हिस्सों पर सील करना
JSON Web Token की एन्कोडिंग वाली तरफ़ दो सीलिंग और एक सिग्नेचर है, और वह सीलिंग है आपका base64url एक्सटेंशन, पैडिंग गिराकर, जो बस इसी फ़ॉर्मेट की मांग है। हेडर और पेलोड दोनों JSON डॉक्यूमेंट्स हैं, और दोनों हिस्सों को वही इलाज मिलता है:
import Foundation
extension Data {
var base64URLEncoded: String {
base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
func seal(_ text: String) -> String {
Data(text.utf8).base64URLEncoded
}
let header = seal(#"{"alg":"HS256","typ":"JWT"}"#)
let claims = seal(#"{"sub":"42","role":"editor"}"#)
print("\(header).\(claims).signature-here")
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsInJvbGUiOiJlZGl0b3IifQ.signature-here
दो यादें। तीसरा डॉट-अलग हिस्सा एक क्रिप्टोग्राफ़िक सिग्नेचर है जो पहले दो पर कंप्यूट की गई है, और वह टोकन का वही हिस्सा है जो कोई भी गैरंटी देता है: हेडर और क्लेम्स सादे JSON हैं, बस ट्रेंच कोट पहने, इसलिए गुप्त चीज़ें कभी भी उनके अंदर नहीं जातीं। और नज़र रखिए कि seal() में पैडिंग कैसे गायब हो जाती है: दूसरी तरफ़ के JWT डिकोडर्स (sister लेख वाला समेत) उसे मॉड्यूलो टॉप-अप से वापस जोड़ देते हैं, इसलिए सफ़र की दोनों दिशाएँ एक ही ज़मीन पर मिल जाती हैं।
HTTP हेडर्स: Basic और बाक़ी
पुराना Authorization: Basic हेडर एक यूज़रनेम और पासवर्ड माँगता है, जो कॉलन से जुड़े हों, स्टैंडर्ड base64 से पैक किए गए, क्योंकि हेडर में + और / बेख़तर हैं और डायलेक्ट का सवाल ही खड़ा नहीं होता:
import Foundation
let credentials = "editor:s3cret"
let header = "Basic " + Data(credentials.utf8).base64EncodedString()
print("Authorization: " + header)
// Authorization: Basic ZWRpdG9yOnMzY3JldA==
हर जगह वाली वही ज़ोरदार फुटनोट: पैकिंग खुद ज़ीरो सुरक्षा देती है, और हेडर बस उतना ही सुरक्षित है जितना वह HTTPS कनेक्शन जो उसे साथ ले रहा है। मॉडर्न जुड़वाँ, Authorization: Bearer, इसकी जगह JWT साथ रखता है, इसलिए JWT सेक्शन की सीलिंग वाली रेसिपी वही है जो वहाँ वायर पर जाती है। HTTP में डायलेक्ट का सवाल खड़ा होने वाली एक जगह है क्वेरी स्ट्रिंग: अगर आपका API किसी आइडेंटिफायर को URL में सवार होने देता है, तो वह आइडेंटिफायर base64url होना चाहिए, या कम-से-कम पर्सेंट-एन्कोडेड स्टैंडर्ड base64, कभी न राव स्टैंडर्ड वर्णमाला जिसका + स्पेस पढ़ने को छोड़ दिया जाए।
ईमेल एटैचमेंट्स: 76-चर वाला कॉन्ट्रैक्ट
जब आपका ऐप ऐसा एटैचमेंट बनाता है जो SMTP के 7-बिट जन्म से जीवित बचना चाहिए, तो कॉन्ट्रैक्ट MIME का है: base64, CRLF लाइन एंडिंग्स के साथ 76 चरों पर रैप किया हुआ, और एक Content-Transfer-Encoding: base64 हेडर जो रिसीवर को बताता है कि क्या उम्मीद करें। ऑप्शन वाला सेक्शन ने स्पेलिंग पहले से दिखा दी है; यहाँ रैप की हुई बॉडी का पूरा रूप है:
import Foundation
let attachment = Data((0..<400).map { UInt8(65 + $0 % 26) })
let body = attachment.base64EncodedString(options: [.lineLength76Characters,
.endLineWithCarriageReturn, .endLineWithLineFeed])
let lines = body.components(separatedBy: "\r\n")
print(lines.count) // 8 लाइनें
print(lines.map { $0.count }.max() ?? 0) // 76, सबसे लंबी
print(body.hasSuffix("\r\n")) // false, आख़िरी लाइन बिना एंडिंग के रहती है
इस डायलेक्ट का साइज़ बिल वह मशहूर वाला है: 4/3 वर्णमाला टैक्स और हर 76 चरों पर एक लाइन ब्रेक मूल आकार के करीब 137 प्रतिशत तक पहुँचा देता है, और पुराने मेल-इंजीनियरिंग शॉर्टकट "मूल को 1.37 से गुणा करो और हेडर्स के लिए आर-पार 800 बाइट्स जोड़ दो" अब भी मेल क्लाइंट में एटैचमेंट साइज़ आँख-बत्ती से आँकने के लिए काम आता है। यह सही अंकगणित वाला फॉलकलोर है, और यही इस आर्टिकल की वह एक जगह है जहाँ 33 प्रतिशत का चार्ज दूसरा दशमलव उगा लेता है।
कॉन्फ़िग, एनवायरन्मेंट और डेटाबेस: अंडरस्कोर छुपाना
एक चुप-चाप क्लास कामों की है जहाँ base64 की एकमात्र ख़ूबी यह है कि उसका आउटपुट एक छोटी, पूर्वानुमानयोग्य कैरेक्टर सेट है: बायनेरी ब्लॉब या स्ट्रक्चर्ड वैल्यू को किसी ऐसे ठिकाने में छुपाना जो सादा टेक्स्ट चाहता है। एनवायरन्मेंट वेरिएबल्स जो शेल कॉन्फ़िग फ़ाइल से गुज़र कर जीवित बचनी हों, डेटाबेस की कॉलमन्स जहाँ varchar, ब्लॉब से बेहतर लगे, LDAP फ़ाइल जिसका अपना base64 मार्कर हो, QR कोड जो बिट्स की बजाय अक्षरों को ज़्यादा भरोसेमंदता से स्कैन करे। पैटर्न हर जगह वही है: बाइट्स तय करो, एन्कोड करो, स्ट्रिंग स्टोर करो, दूसरे सिरे पर डिकोड करो।
import Foundation
struct FeatureFlags: Codable {
var betaToolbar: Bool
var maxRetries: Int
}
do {
let flags = FeatureFlags(betaToolbar: true, maxRetries: 5)
let json = try JSONEncoder().encode(flags)
let storable = json.base64EncodedString()
print(storable)
guard let packed = Data(base64Encoded: storable) else {
print("decode failed, that is odd")
exit(1)
}
let restored = try JSONDecoder().decode(FeatureFlags.self, from: packed)
print(restored.betaToolbar, restored.maxRetries)
} catch {
print(error)
}
यहाँ दो गड्ढे बसे हैं। पहला है दोहरी रैप: दो इंटीग्रेशन लेयर्स जो दोनों "मददगार बनी" एन्कोड कर देती हैं, इसलिए जो वैल्यू आप स्टोर करते हैं वह base64 की base64 है, और वह पाठक जो एक बार डिकोड करता है, उसे अक्षरों की दीवार मिलती है और वह सोचता है कि फ़ीचर टूट गया है। बस एक बार एन्कोड कीजिए, बस एक बाउंडरी पर, और कमेंट में कह दो। दूसरा है एनवायरन्मेंट से डायलेक्ट ड्रिफ़: अगर वैल्यू कभी भी URL से, फ़ॉर्म फ़ील्ड से, या ऐसे शेल से गुज़रेगी जो + और / के साथ खिलवाड़ कर दे, तो base64url की स्पेलिंग स्टोर कीजिए, क्योंकि चरसेट ही इस फ़ॉर्मेट का पूरा मक़सद है।
फ़ाइलें: .b64 राउंड ट्रिप
वह काम, "इस फ़ाइल को .b64 टेक्स्ट फ़ाइल में बदलो", है पढ़ो, एक कॉल, और लिखो:
import Foundation
let source = URL(fileURLWithPath: "photos/cat.png")
let archive = URL(fileURLWithPath: "photos/cat.b64")
let bytes = try Data(contentsOf: source)
try Data(bytes.base64EncodedString().utf8).write(to: archive)
// बाद में, शायद दूसरे process में
let packed = try String(contentsOf: archive, encoding: .utf8)
let restored = Data(base64Encoded:
packed.trimmingCharacters(in: .whitespacesAndNewlines))
if let restored = restored {
try restored.write(to: URL(fileURLWithPath: "photos/cat-copy.png"))
} else {
print("the .b64 file was not base64 after all")
}
वापसी के सफ़र में वह trimmingCharacters इसलिए है क्योंकि जो भी फ़ाइल लिखने वाला था, उसने शायद एक लाइन एंडिंग जोड़ दी थी, और सख़्त डिकोडर आख़िरी नई-लाइन को nil के फैसले के बराबर मानता है। वह राउंड ट्रिप बाइट-दर-बाइट वापस आता है, और इसे पहली बार ही, जब आप शिप करें, वरिफ़ाई कर लेना चाहिए। जितनी बड़ी फ़ाइलें कि मेमोरी यूज़ दिलचस्प बन जाए, उनके लिए पूरा बफ़र एक साथ एन्कोड न कीजिए। Base64 में एक प्यारी प्रॉपर्टी है जो स्ट्रीमिंग को बिल्कुल सही बना देती है: हर तीन इनपुट बाइट्स चार स्वतंत्र आउटपुट चर बनाते हैं, इसलिए जब तक आपका हर एन्कोड किया गया चंक तीन बाइट्स का गुणज हो, जोड़ा गया आउटपुट पूरी फ़ाइल को एक ही बार में एन्कोड करने जैसा ही होता है। एलाइनमेंट टूटो तो आउटपुट बदल जाता है, क्योंकि चंक बाउंड्री तीन-बाइट ग्रुप को रास्ते में ही तोड़ देता है:
import Foundation
func streamEncode(_ input: InputStream, output: OutputStream, lineLength: Int = 76) throws {
input.open()
output.open()
defer { input.close(); output.close() }
var buffer = [UInt8](repeating: 0, count: 65_536)
var pending = [UInt8]()
var line = ""
var lineCount = 0
func addText(_ text: String) {
line += text
while line.count > lineLength {
if lineCount > 0 { _ = output.write(Array("\r\n".utf8), maxLength: 2) }
_ = output.write(Array(String(line.prefix(lineLength)).utf8), maxLength: lineLength)
line = String(line.dropFirst(lineLength))
lineCount += 1
}
}
func flushGroup(_ group: [UInt8]) {
addText(Data(group).base64EncodedString())
}
while input.hasBytesAvailable {
let n = input.read(&buffer, maxLength: buffer.count)
if n < 0 { throw CocoaError(.fileReadUnknown) }
if n == 0 { break }
pending.append(contentsOf: buffer[0..<n])
let groups = pending.count / 3
if groups > 0 {
flushGroup(Array(pending[0..<(groups * 3)]))
pending.removeFirst(groups * 3)
}
}
if !pending.isEmpty {
flushGroup(pending)
}
if !line.isEmpty {
if lineCount > 0 { _ = output.write(Array("\r\n".utf8), maxLength: 2) }
_ = output.write(Array(line.utf8), maxLength: line.utf8.count)
}
}
फ़ाइल कितनी भी बड़ी हो, पीक मेमोरी है एक रीड बफ़र और मौजूदा लाइन, और रैप किया हुआ आउटपुट वन-शॉट .lineLength76Characters स्पेलिंग के बिल्कुल बराबर होता है। वही तीन-का-गुणज वाला नियम, भूमिकाएँ उलटी करके, वही है जिस पर sister लेख का स्ट्रीमिंग डिकोडर टिका है, इसलिए सफ़र के दोनों पक्ष एक ही गणितीय सच को साझा करते हैं।
बड़े पेलोड और मेमोरी का बिल
चलिए वह अंकगणित करते हैं जो आपको अगली बार ज़रूर लगेगा, जब कोई पूछे "क्या हम इसे base64 कर सकते हैं?" हर तीन इनपुट बाइट्स चार आउटपुट चर बन जाते हैं, इसलिए साइज़ 4/3 से गुणा होती है: 100-किलोबाइट की फ़ाइल 133,336 चरों की स्ट्रिंग बन जाती है, 10-मेगाबाइट की फ़ाइल 13,333,336 चरों की, और इसी तरह। पैडिंग बस आख़िर में ज़्यादा से ज़्यादा दो चर जोड़ती है, जो कुछ बाइट्स से बड़ी किसी भी चीज़ पर राउंडिंग एरर है, और खाली इनपुट ही एकमात्र मुआफ़ी है, जहाँ टैक्स ऑफ़िस एक मुफ़्त पास दे देता है और नतीजा खाली स्ट्रिंग है। तीन प्रैक्टिकल नतीजे। पहला: शुरू करने से पहले बजट बना लो: अगर आपका पेलोड पहले से ही किसी लिमिट के पास है (URL की करीब 2,000 चरों की आराम-दायक सीमा, JSON फ़ील्ड का कॉन्ट्रैक्ट, डेटाबेस कॉलम की चौड़ाई), तो एन्कोड करने से पहले लिमिट को 1.33 से भाग दो, बाद में नहीं (और रैपिंग शामिल हो तो 1.37 से)। दूसरा: जब आप पैक करते हैं, तो मूल बाइट्स और पैक्ड स्ट्रिंग दोनों एक साथ हाथ में होते हैं, इसलिए वर्किंग सेट मूल का करीब 2.33 गुना होता है, और ऊपर की स्ट्रीमिंग फंक्शंस वही एस्केप हैच हैं जब वह आँकड़ा आराम-दायक न रहे। तीसरा: टैक्स अमल में वन-वे है: आप उसे तभी चुकाते हैं जब पैक करते हैं, और आपकी बाइट्स तभी वापस आती हैं जब कोई अनपैक करे, इसलिए असली सवाल कभी "क्या base64 महंगा है?" नहीं, बल्कि "क्या वह सिर्फ़-टेक्स्ट राह जिस पर मैं हूँ, इसे माँगती है?" होता है।
वह ग़लतियाँ जो काटती हैं
- फ़ेलेबल चरसेट स्टेप।
String.data(using:)nilजवाब दे सकता है (.asciiको एक्सेंट वाले चर के साथ आज़माइए), और उसे फोर्स-अनराप करना ख़राब इनपुट को क्रैश हुए ऐप में अपग्रेड करने का क्लासिक तरीका है। कन्वर्ज़न को गार्ड कीजिए, बस base64 कॉल को नहीं, जो आसान हिस्सा है। - CRLF हाउस स्टाइल। लाइन-एंडिंग ऑप्शन के बिना कोई भी
.lineLengthऑप्शन डिफ़ॉल्ट रूप में CRLF बनाता है। अगर आपके फ़ॉर्मेट को बस LF चाहिए और आपने ऑप्शन भूल गए, तो आपका आउटपुट उन कैरिज रीटर्न्स के साथ बनेगा जो उसे कभी नहीं होने थे। - बस-CR वाला फँदा।
.endLineWithCarriageReturnअकेला पुराने Mac की शक्ल वाले बस-CR लाइन एंडिंग्स बनाता है। अगर आपका मतलब CRLF था (और MIME के लिए होता है), तो दोनों लाइन-एंडिंग ऑप्शन्स पास कीजिए। - JSON के अंदर रैप। JSON स्ट्रिंग के अंदर राव लाइन ब्रेक अमान्य JSON है, क़तर-बंद। डॉक्यूमेंट में इंटरपोलेट किया गया रैप किया हुआ base64, base64 लहज़े वाला सिंटैक्स एरर है। रैप किया हुआ आउटपुट को ईमेल बॉडीज़ और सर्टिफिकेट फ़ाइलों में ही रखिए।
- BOM हिचहाइकर। सादा
.utf16कन्वर्ज़न सामने एक दो-बाइट का BOM जोड़ देता है जो आपके पैक्ड आउटपुट में सफ़र करता है और उन डिकोडर्स को उलझा देता है जिन्हें उसकी उम्मीद नहीं थी। जब आपको टैग के बिना UTF-16 चाहिए, तो.utf16LittleEndianया.utf16BigEndianइस्तेमाल कीजिए। - डायलेक्ट ड्रिफ़। स्टैंडर्ड और base64url अलग-अलग वर्णमालाएँ हैं, और RFC इसे लिखित रूप में कहता है। क्वेरी स्ट्रिंग में बचकर पहुँचा
+स्पेस बन जाता है; लेनिएंट स्टैंडर्ड डिकोडर तक पहुँचा-हट जाता है। बाउंडरी पर डायलेक्ट चुनिए और उसे ही बरकरार रखिए। - केस एक अक्षर है। वर्णमाला
Aकोaसे अलग मानती है। केस-फोल्डड कॉपी-पेस्ट या बेहद जोश वाली अपरकेसिंग कॉल चुपचाप डेटा को खराब कर देती है, क्योंकि दोनों वर्ज़न्स हर वर्णमाला चेक पार कर लेते हैं। Base64 केस-सेंसीटिव है बस वैसा ही जैसे पासपोर्ट नंबर होता है। - एलाइनमेंट नियम। स्ट्रीमिंग एन्कोडर्स को चंक्स तीन बाइट्स के गुणजों पर काटना होता है। एलाइनमेंट-हीन चंक आउटपुट बदल देता है, और वह बदलाव चुपचाप होता है: स्ट्रिंग फिर भी डिकोड होती है, ग़लत डेटा में।
- दोहरी रैप। दो लेयर्स जो दोनों एन्कोड करती हैं, base64 की base64 बनाती हैं। वही पाठक जो एक बार डिकोड करता है, जहाँ बाइट्स होने चाहिए वहाँ अक्षर देखता है, और इंसीडेंट खुद को ही लिख लेती है।
- उपलब्धता की दीवार। नए नेटिव ऑप्शन्स (
.base64URLAlphabet,.omitPaddingCharacter) नवीनतम SDK बेटा पर और ओपन-सोर्स Foundation में उपलब्धता मार्कर के पीछे मौजूद हैं, पर उस हर टूलचेन पर नहीं जो आपकी CI छुएगी। अगर आप इन्हें अपनाने लगे, तो उपलब्धता चेक्स से गार्ड कीजिए ताकि वही सोर्स पुराने Xcode और Linux पर भी बिल्ड हो। मौजूदा स्टेबल टूलचेन पर, चार-लाइन वाला एक्सटेंशन हर जगह कम्पाइल होता है जहाँ ऑप्शन्स मौजूद नहीं हैं। - Base64 एन्क्रिप्शन नहीं है। अगर ज़रूरत है गुप्तता की, तो आपने ग़लत टूल चुना है, एक पूरा कैटेगरी ग़लत। Base64 का काम बाइट्स को सफ़र पर भेजना है, और वह बस यही काम करता है, कुछ और नहीं।
इसे कैसे शिप करें
- बाइट्स एन्कोड कीजिए, ख्वाब नहीं। मेटॉड कॉल करने से पहले बाइट फ़ॉर्म तय कीजिए, डिफ़ॉल्ट रूप में UTF-8, और जब वह न हो तो एक्सप्लिसिट रूप से नाम दीजिए, और फ़ेलेबल
data(using:)स्टेप को गार्ड कीजिए, क्योंकि वही वह जगह है जहाँ डेटा असल में गायब होता है। - डिफ़ॉल्ट रूप में बिना रैप, रैप तब कॉन्ट्रैक्ट से। सादा सिंगल-लाइन आउटपुट JSON, APIs, और अधिकांश डेटाबेस के लिए सही है; 64/76 रैपिंग ऑप्शन्स की तरफ़ बस तब ही झुके जब ग्रहण करने वाला फ़ॉर्मेट उन्हें माँगे, और जब आपका मतलब CRLF हो तो दोनों लाइन-एंडिंग ऑप्शन्स के लिए भुगतान कीजिए।
- हर बाउंडरी के लिए एक डायलेक्ट। टेक्स्ट-केंद्रित ठिकानों के लिए स्टैंडर्ड base64, किसी भी चीज़ के लिए जो URL या फ़ाइल-नाम को छूने वाली हो, base64url; कभी दोनों एक ही डॉक्यूमेंट में। कन्वर्ज़न एक बार लिखिए, उसे ईमानदारी से नाम दीजिए, और दोबारा इस्तेमाल कीजिए।
- अतिरिक्त चार्ज का बजट बनाइए। शुरू करने से पहले 4/3 से गुणा कीजिए (रैपिंग खेले में हो तो 1.37 से), और जब पेलोड इतना बड़ा हो कि वर्किंग सेट आराम-दायक न रहे, तो तीन-बाइट एलाइनड चंक्स से स्ट्रीम कीजिए।
- पैकिंग टेप को लॉक की तरह मत इस्तेमाल कीजिए। अगर ज़रूरत है गुप्तता की, तो base64 की शेल्फ़ पर रुकिए और उसकी बजाय एन्क्रिप्शन उठा लीजिए।
पैकिंग का छोटा सा इतिहास
जिस वर्णमाला से आप पैक करते हैं और जिन लाइन लंबाईयों पर आप रैप करते हैं, वे चार दशकों की बहसों की पत्थर-बनीं चीज़ें हैं, कि सिर्फ़-टेक्स्ट राह पर बायनेरी कितना बच सकता है, और Swift का उस इतिहास में स्थान छोटा पर दिलचस्प है:
- 1980 के दशक, वही-मशीन का दौर। इस परिवार के सबसे पहले एन्कोडर्स डायल-अप पर फ़ाइलें उन सिस्टम के बीच ले जाने के लिए मौजूद थे जो मानते थे कि दूसरी तरफ़ की मशीन उनके जैसी ही है। UNIX पर uuencode अपेस अक्षरों, अंकों, और पंक्तिचिह्नों की वर्णमाला इस्तेमाल करता था, और इसके डिज़ाइनर्स ने एक चाल पाई जो कंप्यूटिंग पावर बचाती थी: वर्णमाला लगातार ASCII पोज़िशन पर बैठती है, इसलिए एन्कोडिंग बस "32 जोड़ दो" थी, कोई लुकअप टेबल नहीं। BinHex, वह जुड़वाँ जो 1981 में TRS-80 पर जन्मा, Apple II पर कूदा, 1984 में क्लासिक Macintosh का फ़ॉर्मेट बन गया, और एक अलग दांव खेला: उसके 64 चर
7,O,W,g,o, और लोअरकेस का आधा-अधूरा हिस्सा छोड़ देते हैं। - 1987, वर्णमाला को पता मिला। RFC 989, पहली Privacy-Enhanced Mail स्पेसिफिकेशन, ने वह बिल्कुल 64 चरों की वर्णमाला स्टैंडर्ड बनाई जिसे आप आज टाइप करते हैं, आउटपुट को 64 चरों प्रति लाइन रैप किया, और पैडिंग के लिए
=इस्तेमाल किया, एन्कोड किया हुआ पर एन्क्रिप्ट न हुआ डेटा को चिह्नित करने के लिए*। वह हर PEM-style ब्लॉक जो आपने कभी सर्वर कॉन्फ़िग में पेस्ट किया, इसी डॉक्यूमेंट की संतान है। - 1996, ढीला दौर। MIME (RFC 2045) ने वर्णमाला को ईमेल एटैचमेंट्स के लिए लिया और रैप को 76 चरों पर ले गया, उस नियम के साथ जो रैप को प्रोड्यूस करने में सुरक्षित बना दिया: डिकोडर्स को लाइन ब्रेक्स नज़रअंदाज़ करने चाहिए थे। एन्कोडर्स ने रैप करना सीखा; डिकोडर्स ने माफ़ करना। Swift का 76-चर ऑप्शन बस इसी बहस की जीता-जागती यादगार चीज़ है।
- 2003 से 2006, नियम सख़्त होते हैं। RFC 3548 (2003) ने घोषणा की कि पैडिंग को छोड़ा नहीं जा सकता (जब तक कोई फ़ॉर्मेट कुछ और न कहें) और डिकोडर्स को वर्णमाला के बाहर के चरों को रिजेक्ट करना होगा; RFC 4648 (अक्टूबर 2006) ने परिवार की बात तय की और URL-safe वर्णमाला जोड़ी, एक्सप्लिसिट रूप से, ताकि लंबे आइडेंटिफायर्स URLs में बिना हर स्पेशल चर को पर्सेंट-एस्केप किए रह सकें। "URL डायलेक्ट में पैडिंग नहीं" वाला कन्वेशन उसी डॉक्यूमेंट में जन्मा, क्योंकि URL में पैड चर आमतौर पर
%3Dबन जाता है, और मक़सद ही बेकार हो जाता है। - 2013 से 2014, API पहले से यहाँ है। Apple की
NSDataक्लास सालों से base64 पैक कर रही थी, और ऑप्शन-आधारित API, चारों रैपिंग ऑप्शन्स के साथ, 2013 में iOS 7 में आया, Swift के अस्तित्व में आने से पहले। जब 9 सितंबर 2014 को Swift 1.0 आया, तो उसने वारसा में एक टोटल एन्कोडर लिया, चार रैपिंग ऑप्शन्स और 1987 की 64-अक्षरों की वर्णमाला के साथ, और तब से पर्सनालिटी बदली नहीं है। - 3 दिसंबर 2015, टूलचेन इमारत छोड़ता है। उसी दिन Swift को ओपन-सोर्स किया गया, और Foundation का base64 उसके साथ Linux पर और बाद में Windows पर पहुँचा। "Apple मशीन से दूर Swift में Base64 encoding" की उम्र एक दशक भी नहीं हुई: वह बहुत छोटी उम्र का मेहान, जिस पार्टी में 1987 में शुरुआत हुई थी।
- 2023 से 2026, फिर से लिखाई और URL डायलेक्ट। Foundation का रीराइट (swift-foundation प्रोजेक्ट) ने
Dataको शुद्ध-Swift कोर में ले गया, और 2025 में एक कम्युनिटी प्रस्ताव ने नेटिव base64url और पैडिंग-हटाने वाले ऑप्शन्स जोड़े। इस लेख के लिखे जाने पर, नवीनतम SDK बेटा और ओपन-सोर्स टूलचेन एन्कोडिंग ऑप्शन्स के साथ शिप कर रहे हैं, परिवार का बाक़ी हिस्सा ओपन-सोर्स Foundation में उपलब्धता मार्कर के पीछे परिपक्व हो रहा है, और इंतज़ार के समय कम्युनिटी एक्सटेंशन ही पोर्टेबल सेतु बनी हुई है।
छोटी-छोटी ख़ुशीयाँ
- एक मेगाबाइट ठीक 1,333,336 base64 चरों में पैक होती है, 4/3 टैक्स और दो पैडिंग चर समेत, अंक-दर-अंक तक। टैक्स से बिल्कुल बचने वाला एकमात्र इनपुट खाली वाला है: कुछ नहीं गया, कुछ नहीं आया।
- एन्कोडर टोटल है, ऐसे जिस तरह डिकोडर नहीं है। यह कभी nil नहीं रिटर्न करता, कभी थ्रो नहीं करता, कभी मना नहीं करता। पूरी पाइपलाइन में यही एकमात्र फ़ेलियर अपस्ट्रीम में, चरसेट स्टेप में बसा है, और इसीलिए यह मेटॉड अपने जुड़वाँ से कहीं ज्यादा शांत लगती है।
- शब्द
hélloको UTF-8 में एन्कोड कीजिए तो वहaMOpbGxvबन जाता है, और UTF-16 लिटिल-एंडियन में एन्कोड कीजिए तोaADpAGwAbABvAA==। वही शब्द, दो अलग-अलग पासपोर्ट, दोनों मान्य, और न कोई भी एक-दूसरे से बदल सकता है। - base64 दुनिया का टेस्ट वर्ड
foobarहै, और वहZm9vYmFyमें पैक होता है। अगर आपने कभी भी base64 का कोई उदाहरण खुले आम देखा है, तो foobar के साथ होने के पूरे चाँस हैं। - मशहूर 1x1 ट्रांसपेरेंट GIF 42 बाइट्स का है और मैजिक वर्ड
GIF89aसे शुरू होता है, इसीलिए प्रिफ़िक्सR0lGODlhइस धरती के लगभग हर किसी base64 स्ट्रिंग से ज़्यादा कोडबेस में दिखता है। - आपके
Codableसट्र्ख्स ने शायद सालों से base64 भेजा है, बिना आपको पता चले:JSONEncoderका डिफ़ॉल्टDataसट्र्टेजी स्टैंडर्ड base64 से पैक करता है, इसीलिएDataफ़ील्ड वायर पर अंकों की सेरी के बजाय पैडेड स्ट्रिंग के रूप में जाता है। - पैडिंग कभी भी 2 चरों से आगे नहीं जाती। कभी भी नहीं। 1-बाइट का पेलोड
==से ख़त्म होता है, 2-बाइट का=से ख़त्म होता है, और 3-बाइट का पेलोड बिना किसी एंडिंग के ख़त्म होता है। अंतिम ग्रुप का पूरा ग्रामर नाखून पर बैठ जाता है। - Swift उस वर्णमाला से 27 साल छोटा है जिससे वह पैक करता है। भाषा 2014 में शिप हुई; 64 अक्षर 1987 में स्टैंडर्ड बने और तब से बदले नहीं।
यही पूरा पैकिंग टूलबॉक्स है: एक टोटल मेटॉड, एक फ़ेलेबल स्टेप जो उसके आगे आता है, CRLF हाउस स्टाइल के चार रैपिंग ऑप्शन्स, 4-लाइन की base64url एक्सटेंशन, स्ट्रीमिंग के लिए 3-बाइट एलाइनमेंट नियम, और 4/3 का अतिरिक्त चार्ज जो सिर्फ़-टेक्स्ट राह का एंट्री फ़ी है। एन्कोडिंग वह तरफ़ है जहाँ आप base64 का बिल चुकाते हैं, और अब हस्ताक्षर से पहले आपको बिल की हर पंक्ति पता है। जैसे ही आप सफ़र को उल्टा करके दूसरों के पैक किए हुए को खोलना शुरू करेंगे, nil रिटर्न्स, व्हाइटस्पेस वर्डिक्ट्स, और लेनिएंट नॉब का अंधा कोण मंच पर चढ़ जाएँगे। रिलेटेड डिकोडिंग आर्टिकल उस राउंड ट्रिप की उस आधी पर पूरा शो खेला है, इसलिए जब अक्षर आने लगेंगे, तो आप बस इतना ही जानते होंगे कि उन्हें ठीक-ठीक कैसे खोलना है।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: Swift में Base64 डिकोडिंग: एक सम्पूर्ण गाइड