R में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड
आपके पास ऐसे बाइट्स हैं जिन्हें यात्रा करनी है, और राह सिर्फ़ टेक्स्ट को जाने देती है। एक JPEG जिसे JSON फ़ील्ड के अंदर बसना पड़े। एक सर्टिफ़िकेट जिसे एनवायरनमेंट वेरिएबल में बैठना पड़े। एक प्लॉट जिसे self-contained HTML रिपोर्ट के अंदर यात्रा करनी पड़े। Base64 सबके लिए वह पैकिंग टेप है: बाइट्स की कोई भी श्रृंखला 64 बेख़तरा चरों की स्ट्रिंग बन जाती है जो हर उस टेक्स्ट चैनल में बच निकलती है जिसे आप उस पर फेंक सकते हैं। इस साइट का होम पेज फ़ॉर्मैट को पूरा-पूरा कवर करता है, इसलिए यहाँ है छोटा संस्करण: तीन बाइट्स अंदर जाते हैं, चार चर बाहर आते हैं, A से Z, a से z, 0 से 9, और साथ में + और /, और पूंछ पर थोड़ी सी = पैडिंग, जब सामान समान बँट न सके।
R-ख़ास मोड़: बेस R में Base64 एन्कोडर की बात ही नहीं है। किसी बेस पैकेज में इंतज़ार में खड़ा कोई base64_encode() नहीं है, न कोई वन-लाइन बिल्टइन जिसका सहारा लिया जा सके। आप एक पैकेज चुनते हैं, और एकोसिस्टम सच में आपको विकल्प देता है: अलग-अलग स्पीड्स, अलग-अलग रैपिंग की आदतें, और पैडिंग पर अलग-अलग राय। इस लेख के अंत तक आपको पता हो जाएगा कि हर हालात में किस एन्कोडर का सहारा लें, और उनमें से कौन चुपचाप एन्कोड करने के बाहर कुछ और कर देगा।
एन्कोडर परिदृश्य
एन्कोडिंग पाँच पैकेज करते हैं, और वे रोज़मर्रा के कामकाज के कामगार, क्रिप्टो से जुड़े, और छोटे विशेषज्ञों में बँट जाते हैं। यहाँ है पूरा दल, 2026 के हिसाब से ताज़ा:
| पैकेज | वर्ज़न (2026) | एन्कोड एंट्री पॉइंट | रैपिंग की आदतें | इसे कब इस्तेमाल करें |
|---|---|---|---|---|
base64enc |
0.1-6 | base64encode() |
linewidth और newline, पूरी तरह आपकी सेट करने के लिए |
रोज़मर्रा की स्ट्रिंग्स, MIME रैपिंग |
openssl |
2.4.2 | base64_encode() |
64-चर पंक्तियाँ, LF ब्रेक्स, ट्रेलिंग न्यूलाइन | PEM फ़ाइलें, मौजूदा क्रिप्टो स्टैक्स |
b64 |
0.1.7 | encode(), encode_file() |
कभी रैप नहीं करता; b64_chunk() और b64_wrap() ज़रूरत पर |
स्पीड, वेक्टर, URL-सुरक्षित इंजन |
base64 |
2.0.2 | encode() |
64-चर पंक्तियाँ और डिफ़ॉल्ट रूप से ट्रेलिंग न्यूलाइन | फ़ाइल-से-फ़ाइल काम, रिपोर्ट की इमेज |
base64url |
1.4 | base64_urlencode() |
कभी रैप नहीं करता, कोई पैडिंग नहीं, चर अंदर | URL-सुरक्षित स्ट्रिंग्स |
तीन और एन्कोडर उन पैकेजों के अंदर छिपे हैं जो आप शायद पहले से लोड करते हैं। jsonlite base64_enc() और base64url_enc() एक्सपोर्ट करता है, इसलिए अगर आप पहले से JSON पार्स करते हैं, तो शायद आपके पास पहले से एक एन्कोडर हाथ में हो। jose JWT काम के लिए base64url_encode() एक्सपोर्ट करता है। और वीटरान RCurl पैकेज आज भी libcurl के चारों ओर base64() रैपर साथ रखता है जो बिल्कुल ठीक चलता है और एक पिछले युग का है। base64 पैकेज, आख़िरकार, अब अपने ही पैकेज के लेबल पर खुद को एक compatibility wrapper बताता है और नई ऐप्लिकेशन को base64enc, openssl या jsonlite की ओर ले जाता है।
सेटअप
अगर मशीन पर अभी R नहीं है, तो आपका ऑपरेटिंग सिस्टम इसे साथ देता है: Debian और Ubuntu पर r-base, Fedora पर R, macOS पर Homebrew या आधिकारिक इंस्टॉलर, Windows पर एक इंस्टॉलर। फिर एन्कोडर, सीधे CRAN से:
install.packages("base64enc")
install.packages("openssl")
install.packages("b64")
install.packages("base64url")
इंस्टॉल की गड़बड़ी के दो ठिकाने, दोनों बिल्ड टाइम पर। openssl आपके सिस्टम OpenSSL के साथ कंपाइल होता है, इसलिए साफ़ Linux बॉक्स को पहले डेवलपमेंट हेडर्स चाहिए होंगे (sudo apt install libssl-dev), या Debian और Ubuntu पर sudo apt install r-cran-openssl से आप कंपाइलेशन पूरी तरह छोड़ सकते हैं। और b64 extendr से रैप किया गया Rust इंजन है, इसलिए सोर्स बिल्ड को Rust टूलचैन चाहिए (sudo apt install cargo के साथ rustc भी आ जाती है)। Windows और macOS को CRAN से प्री-बिल्ट बायनेरी मिलती हैं और इसमें से कुछ भी लागू नहीं होता।
पहला एन्कोड
एन्कोडिंग की ज़िंदगी का नब्बे प्रतिशत तीन पंक्तियों में समा जाता है, वही मशहूर स्ट्रिंग इस्तेमाल करते हुए जो डिकोड की तरफ अपना स्मोक टेस्ट बनाती है:
library(base64enc)
packed <- base64encode(charToRaw("Man"))
packed
#> [1] "TWFu"
identical(packed, "TWFu")
#> [1] TRUE
उस रस्म में तीन बातें नोट करने लायक हैं। पहली, इनपुट एक raw वेक्टर है: charToRaw() आपकी R स्ट्रिंग से उन बाइट्स तक की सेतु है जो पैक होते हैं, और इस टेबल के रोज़मर्रा के एन्कोडर - base64enc, openssl, b64 - सब raw लेते हैं। दूसरी, आउटपुट डिकोडरों की उल्टी दिशा है: एक ही character स्ट्रिंग, क्योंकि एन्कोडिंग सीमा के टेक्स्ट की तरफ ख़त्म होती है। तीसरी, लंबाई की गणित को चलाते देखिए: तीन बाइट्स अंदर, चार चर बाहर, कोई पैडिंग ज़रूरी नहीं क्योंकि सामान समान बँट गया। जब सामान बँट न सके, तो एक या दो = चर पूंछ पर उतर जाते हैं।
और क्योंकि भरोसा न करने वाला एन्कोडर किसी से भी बुरा होता है, यहाँ वह राउंड ट्रिप है जो साबित करता है कि दोनों दिशाएँ एकमत हैं:
text <- "Hello, world!"
packed <- base64encode(charToRaw(text))
packed
#> [1] "SGVsbG8sIHdvcmxkIQ=="
identical(text, rawToChar(base64decode(packed)))
#> [1] TRUE
स्ट्रिंग्स, बाइट्स और फ़ाइलनेम का फँसाव
अब वह फँसाव, क्योंकि हर R डेवलपर एक बार उसमें गिरता है। base64encode() एक character आर्ग्यूमेंट को एन्कोड करने के लिए टेक्स्ट नहीं, बल्कि फ़ाइलनेम की तरह मानता है। इसे एक स्ट्रिंग दीजिए और वह उस फ़ाइल को ढूँढने निकल जाता है:
base64encode("Man")
#> Warning in file(what, "rb") :
#> cannot open file 'Man': No such file or directory
#> Error: cannot open the connection
base64encode(charToRaw("Man"))
#> [1] "TWFu"
वॉर्निंग ही पहचान है: उसने file("Man", "rb") आज़माया, यानी "Man नाम की फ़ाइल raw पढ़ने के लिए खोलो"। इसलिए base64encode() के साथ अनुशासन एक रिफ्लेक्स है: पहले हमेशा charToRaw()। अगर फ़ाइल सच में आपका मतलब है, तो वही वह चीज़ है जो फ़ंक्शन कर रहा है, और आउटपुट फ़ाइल के बाइट्स का पैक रूप है, जो कभी-कभी बिल्कुल आपकी मंज़ूर है।
b64 इसी सवाल पर अलग रुख़ रखता है: उसका encode() character वेक्टर सीधे लेता है, हर तत्व को UTF-8 टेक्स्ट की तरह मानता है, और वेक्टराइज़्ड भी है:
b64::encode("Man")
#> [1] "TWFu"
b64::encode(c("Man", "M"))
#> [1] "TWFu" "TQ=="
इसी सीमा की दो और किनारे जानने लायक हैं। ख़ाली इनपुट तीन अलग-अलग तरह से एन्कोड होता है, और यह इस बात पर निर्भर करता है कि आपसे कौन पूछता है:
base64encode(raw(0))
#> character(0)
b64::encode("")
#> [1] ""
base64url::base64_urlencode("")
#> [1] ""
base64enc ख़ाली स्ट्रिंग नहीं, बल्कि ज़ीरो-लंबाई character वेक्टर से जवाब देता है, इसलिए डाउनस्ट्रीम कोड जो स्ट्रिंग उम्मीद करे और character(0) पाए, वह आश्चर्यजनक जगहों पर फ़ेल होता है। और कैरेक्टर सेट का फैसला एन्कोड की तरफ भी बसता है: charToRaw() स्ट्रिंग को उसी एन्कोडिंग में पैक करता है जो वह अभी पहन रही है, इसलिए UTF-8 टेक्स्ट UTF-8 बाइट्स के रूप में यात्रा करता है, जो वायर के दूसरे सिरे की उम्मीद है। इमोजी समेत, क्योंकि आधुनिक R U+FFFF से ऊपर के कोड पॉइंट्स सच्चे UTF-8 के रूप में स्टोर करता है:
emoji <- "\U0001F600"
nchar(emoji, type = "bytes")
#> [1] 4
round_trip <- base64decode(base64encode(charToRaw(emoji)))
identical(emoji, rawToChar(round_trip))
#> [1] TRUE
लाइन रैपिंग: MIME, PEM और आपकी अपनी चौड़ाई
लंबी Base64 स्ट्रिंग्स पंक्तियों में टूटती हैं, क्योंकि दुनिया के सबसे पुराने टेक्स्ट चैनल स्तंभ की सीमा रखते थे, और MIME ने उन्हें भूलने का मन ही नहीं किया। जो दो इतिहासी रैप्स आपको मिलेंगे, वे हैं MIME, जो 76 चरों पर ब्रेक करता है और पंक्तियों के बीच CRLF रखता है, और PEM, जो 64 पर ब्रेक करता है। हर एन्कोडर का अपनी राय है कि इनमें से कौन सा (यदि कोई हो) इस्तेमाल करना है, इसलिए यही वह सेक्शन है जहाँ एन्कोड से पहले आप करार चुनते हैं।
पहले साइज़ की गणित, क्योंकि रैपिंग का सवाल यह जानना है कि आउटपुट कितनी लंबा होता है। हर तीन बाइट्स पर चार चर बाहर आते हैं, जिससे एन्कोडेड रूप की लंबाई की एक सीधी छत बन जाती है:
nchar(base64encode(charToRaw(strrep("a", 100))))
#> [1] 136
4 * ceiling(100 / 3)
#> [1] 136
उसके साथ जेब में, एन्कोडर। base64enc सबसे लचीला है: डिफ़ॉल्ट रूप से वह एक बिना-टूटी पंक्ति देता है, और linewidth आर्ग्यूमेंट पंक्तियों का वेक्टर सौंपता है:
long <- charToRaw(strrep("R", 100))
base64encode(long, linewidth = 76)
#> [1] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJS"
#> [2] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUg=="
base64encode(long, linewidth = 76, newline = "\r\n")
#> [1] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJS\r\nUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUg=="
100 बाइट्स के लिए चौड़ाई 76 पर दो पंक्तियाँ, डिफ़ॉल्ट रूप से वेक्टर, और newline जोड़ने पर एक ही CRLF से जुड़ी स्ट्रिंग। कोई ट्रेलिंग ख़ाली तत्व नहीं: 114 बाइट्स, जो बिल्कुल दो 76-चर पंक्तियों में एन्कोड होते हैं, दो पंक्तियों के रूप में वापस आते हैं, तीन नहीं।
openssl दूसरा ध्रुव लेता है। linebreaks = TRUE साधारण LF ब्रेक्स के साथ 64 चरों पर रैप करता है, और सबसे अंत में एक और न्यूलाइन जोड़ देता है:
wrapped <- openssl::base64_encode(charToRaw(strrep("a", 100)), linebreaks = TRUE)
nchar(wrapped)
#> [1] 139 # 136 डेटा चर प्लस 3 लाइन ब्रेक्स
nchar(strsplit(wrapped, "\n", fixed = TRUE)[[1]])
#> [1] 64 64 8
गणित गिनिए: 136 डेटा चर, दो आंतरिक ब्रेक्स, एक ट्रेलिंग ब्रेक, कुल 139। और वह ट्रेलिंग ब्रेक उस सबसे प्राकृतिक लाइन काउंट के लिए अदृश्य है जो आप लिख सकते हैं, क्योंकि strsplit() ट्रेलिंग ख़ाली टुकड़ा गिरा देता है, इसलिए वेक्टर तीन पंक्तियाँ कहता है जबकि स्ट्रिंग चार उठाती है। अगर आप कभी OpenSSL-रैप्ड आउटपुट और MIME-रैप्ड आउटपुट का diff लें और चरों का हिसाब ना बने, तो यही वह भूत है।
b64 रैपिंग ही नहीं करता; वह दोनों ऑपरेशन अलग-अलग देता है, और चंक चौड़ाई का एक नियम है, चार का गुणांक, क्योंकि इंजन Base64 ग्रुप को आधा काटने से इनकार कर देता है:
enc <- b64::encode(strrep("a", 100))
ch <- b64::b64_chunk(enc, 76)
b64::b64_wrap(ch, "\r\n")
#> [1] "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\r\nYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
b64::b64_chunk(enc, 75)
#> Error: Chunk size must be a multiple of 4.
और फ़ाइल-ओरिएंटेड base64 पैकेज OpenSSL के कदमों पर चलता है: 64-चर पंक्तियाँ, ट्रेलिंग न्यूलाइन, डिफ़ॉल्ट रूप से चालू:
writeBin(charToRaw(strrep("b", 200)), "big.bin")
base64::encode("big.bin", "big.b64")
con <- file("big.b64")
lines <- readLines(con)
close(con)
nchar(lines)
#> [1] 64 64 64 64 12 0
वह आख़िरी ज़ीरो ट्रेलिंग न्यूलाइन है, जो readLines() ने ख़ाली आख़िरी पंक्ति के रूप में पकड़ ली। यहाँ है पूरा मैदान एक साथ:
| एन्कोडर | चौड़ाई | लाइन ब्रेक | ट्रेलिंग न्यूलाइन |
|---|---|---|---|
base64encode(x) |
एक पंक्ति | कोई नहीं | कोई नहीं |
base64encode(x, linewidth = 76, newline = "\r\n") |
76 | CRLF | कोई नहीं |
openssl::base64_encode(x, linebreaks = TRUE) |
64 | LF | हाँ |
b64::encode(x) |
एक पंक्ति | कोई नहीं (b64_chunk() और b64_wrap() से रैप कीजिए) |
कोई नहीं |
base64::encode(in, out) |
64 | LF | हाँ |
base64url::base64_urlencode(x) |
एक पंक्ति | कोई नहीं | कोई नहीं |
URL-सुरक्षित Base64
स्टैंडर्ड Base64 अपनी वर्णमाला के अंतिम दो स्थान + और / पर खर्च करता है, और वही बिल्कुल उन चरों पर हैं जिन्हें URL पसंद नहीं करते: प्लस बन जाता है %2B, स्लैश बन जाता है %2F, और हर = पैडिंग बन जाती है %3D। URL-सुरक्षित रूप, जिसकी परिभाषा RFC 4648 सेक्शन 5 में है, उन दो चरों की जगह - और _ रखता है और आमतौर पर पैडिंग भी गिरा देता है, इसलिए एक टोकन जो हर जगह पेस्ट होना चाहिए, हर जगह पेस्ट रहता है। R के पास इसके लिए तीन दरवाज़े हैं।
b64 के इंजन सबसे पूरे हैं: इंजन एक सेट की गई वर्णमाला और पैडिंग नीति है, और पैकेज चारों इंजन साथ देता है जो आपको चाहिए। वही तीन बाइट्स स्टैंडर्ड इंजन को और URL-सुरक्षित इंजन को खिलाइए और वर्णमाला को काम करता देखिए:
bytes <- as.raw(c(0xfb, 0xef, 0xbe))
b64::encode(bytes)
#> [1] "++++"
b64::encode(bytes, engine("url_safe"))
#> [1] "----"
b64::encode(as.raw(0x4d), engine("url_safe"))
#> [1] "TQ=="
b64::encode(as.raw(0x4d), engine("url_safe_no_pad"))
#> [1] "TQ"
चार इंजन, "standard", "standard_no_pad", "url_safe", और "url_safe_no_pad", और वही इंजन ऑब्जेक्ट दोनों दिशाओं में काम करता है, जिससे आपका कोड सिमेट्रिक रहता है। बिना-पैडिंग वाले रूप तभी अलग दिखते हैं जब पैडिंग सच में मिलने वाली हो, जैसे ऊपर के वन-बाइट उदाहरण में।
समर्पित base64url पैकेज एक-का-काम वाला दरवाज़ा है: चर अंदर, URL-सुरक्षित स्ट्रिंग बाहर, कभी रैप नहीं, कभी पैड नहीं:
base64url::base64_urlencode("hello world")
#> [1] "aGVsbG8gd29ybGQ"
और jose JWT काम के लिए अपना base64url_encode() एक्सपोर्ट करता है, और डिकोड की तरफ raw वेक्टर वापस करता है, ठीक वैसे जैसे आप उम्मीद करेंगे। एक नियम तीनों दरवाज़ों को बँधाता है: जिस वर्णमाला से आप एन्कोड करते हैं, उसी से डिकोड भी करना होगा। स्टैंडर्ड डिकोडर को "----" स्ट्रिंग दीजिए और वह पहले ही डैश पर फ़ेल हो जाएगा; डिकोडिंग का बहन साइट का संगत लेख यह कवर करता है कि हर डिकोडर गंदे या मेल न खाने वाले इनपुट से कैसे मिलता है।
JWTs: टोकन बनाना
JSON Web Token वही जगह है जहाँ URL-सुरक्षित Base64 रोज़मर्रा का साधन बन गया। JWT तीन base64url हिस्से हैं जो बिंदुओं से जुड़े होते हैं: एक हेडर जो बताता है कि यह कैसे साइन हुआ, एक JSON claims का पेलोड, और एक सिग्नेचर जो दोनों को बाँधता है। एन्कोड की तरफ आप तीनों को बना रहे हैं, और jose पैकेज पूरी रस्म संभालता है। उसकी API jwt_* फ़ंक्शनों (jwt_claim(), jwt_encode_hmac(), jwt_decode_hmac(), jwt_split()) के चारों ओर बनी है; 2.0 रिलीज़ (अप्रैल 2026) ED25519 सपोर्ट जोड़ती है और typ हेडर फ़ील्ड को वैकल्पिक बना देती है:
library(jose)
claim <- jwt_claim(iss = "app", sub = "1234567890", exp = Sys.time() + 3600)
token <- jwt_encode_hmac(claim, "0123456789abcdef")
sp <- jwt_split(token)
sp$header
#> $typ
#> [1] "JWT"
#>
#> $alg
#> [1] "HS256"
names(sp$payload)
#> [1] "iss" "sub" "exp" "iat"
नज़र रखिए कि jwt_claim() पीछे-पीछे क्या किया: iat (issued at) डिफ़ॉल्ट रूप से वर्तमान समय होता है, इसलिए वह माँगे बिना ही पेलोड में शामिल हो गया, जबकि exp डिफ़ॉल्ट रूप से कुछ नहीं होता, जिसका मतलब है बिना एक्सपायरी वाला टोकन, जो आमतौर पर आप नहीं चाहेंगे। exp जान-बूझ कर सेट कीजिए, और बाक़ी का ख़्याला सिग्नेचर लेगा। jwt_split() जाँच का औज़ार है: हेडर, पेलोड एक नामी लिस्ट के रूप में, और raw सिग्नेचर, बिल्कुल कोई पुष्टि नहीं, ठीक वही झाँकने वाला कदम जो इस लेख-जोड़े की डिकोडिंग वाली तरफ़ वर्णन करती है।
पुष्टि वाली तरफ ही jose अपना ख़र्चा कमाता है। jwt_decode_hmac() सिग्नेचर की जाँच करता है और समय के claims को लागू करता है, और उसका इनकार करने का अंदाज़ ख़ास है:
round <- jwt_decode_hmac(token, "0123456789abcdef")
round$sub
#> [1] "1234567890"
jwt_decode_hmac(token, "ffffffffffffffff")
#> Error: HMAC signature verification failed!
old <- jwt_claim(sub = "1234567890", exp = Sys.time() - 3600)
expired <- jwt_encode_hmac(old, "0123456789abcdef")
jwt_decode_hmac(expired, "0123456789abcdef")
#> Error: Token has expired on 2026-08-30 05:09:36
कामयाबी पर आप claims को एक साधारण लिस्ट के रूप में वापस पाते हैं, इसलिए round$sub और साथी बस काम करते हैं। भविष्य की nbf (not before) claim को अपना ख़ास इनकार मिलता है, Token is not valid before ..., और HMAC टोकन असममितीय jwt_decode_sig() से डिकोड नहीं होता, जो जवाब देता है Unsupported algorithm: HMAC और बजाय में एक public key चाहता है। दो आख़िरी बातें। पहली, पेलोड एन्कोडेड है, एन्क्रिप्टेड नहीं: हर कोई हर claim पढ़ सकता है, इसलिए कोई रहस्य उसमें बसने का हक़ नहीं रखता। दूसरी, अगर आप httr2 को jose के बाद लोड करते हैं, तो masking मैसेज पर नज़र रखिए: httr2 अपने खुद के jwt_claim(), jwt_encode_hmac() और jwt_encode_sig() एक्सपोर्ट करता है, OAuth client credentials के लिए बने हुए, जिनका exp डिफ़ॉल्ट पाँच मिनट आगे होता है, और वे सेशन के बाक़ी हिस्से में jose के संस्करणों पर छाया डाल देते हैं।
फ़ाइलें: बायनेरी अंदर, टेक्स्ट बाहर
फ़ाइल को एन्कोड करना डिकोड की तरफ की फ़ाइल-काम का दर्पण है, और b64 पैकेज की एंट्री पॉइंट सबसे साफ़ है, जो सबसे तेज़ भी है क्योंकि वह कभी एक बहुत बड़ी मध्यवर्ती स्ट्रिंग नहीं बनाता:
writeBin(charToRaw("file payload bytes"), "payload.bin")
enc <- b64::encode_file("payload.bin")
enc
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz"
cat(enc, file = "payload.b64")
readLines("payload.b64")
#> Warning in readLines("payload.b64") :
#> incomplete final line found on 'payload.b64'
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz"
वॉर्निंग एक छुपे हुए फीचर है: cat() ट्रेलिंग न्यूलाइन नहीं जोड़ता, इसलिए फ़ाइल पंक्ति के बीच में ख़त्म होती है, और readLines() आपको बताता है। लेकिन जब फ़ाइल को b64::decode_file() खाएगा, जो ट्रेलिंग न्यूलाइन पर पैनिक करता है, तब इस तथ्य के किस किनारे पर आप हैं, यह याद रखिए; पूरा क़िस्सा डिकोडिंग लेख में है। cat() या writeBin(charToRaw(enc), path) से लिखिए और किनारा मंद ही रहेगा।
base64 पैकेज शुद्ध फ़ाइल-से-फ़ाइल विकल्प है, मिलती-जुलती फ़ंक्शन जोड़ी के साथ और डिफ़ॉल्ट रूप से OpenSSL-शैली रैपिंग:
base64::encode("payload.bin", "payload2.b64")
readLines("payload2.b64")
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz" ""
वह आउटपुट पाथ वापस करता है, जो लॉगिंग के लिए सुविधाजनक है। और जब मशीन को अंदर से देखना हो, तो मैनुअल पाइपलाइन टेबल के किसी भी एन्कोडर के साथ चलता है: फ़ाइल को raw के रूप में पढ़िए, एन्कोड कीजिए, टेक्स्ट लिखिए, ख़त्म:
bytes <- readBin("payload.bin", what = "raw", n = file.size("payload.bin"))
enc <- base64encode(bytes)
writeLines(enc, "payload3.b64")
identical(enc, readLines("payload3.b64"))
#> [1] TRUE
Data URIs और स्व-पर्याप्त दस्तावेज़
data: URI स्कीम (RFC 2397) एन्कोड की तरफ की सबसे दृश्यमान उपभोक्ता है: एक दस्तावेज़ जो अपनी अपनी सामग्री साथ रखता है, MIME टाइप, "base64" शब्द, और पेलोड, सब एक ही एट्रिब्यूट में। PNG के magic बाइट्स इस पैटर्न को पहचानने योग्य बनाते हैं: बाज़ार में हर Base64 PNG एक जैसे चरों से शुरू होता है, क्योंकि फ़ाइल हेडर 89 50 4E 47 हमेशा वैसे ही एन्कोड होता है:
png_head <- as.raw(c(0x89, 0x50, 0x4e, 0x47))
uri <- paste0("data:image/png;base64,", base64encode(png_head))
uri
#> [1] "data:image/png;base64,iVBORw=="
tag <- paste0("<img src=\"", uri, "\" />")
अगर आपने कभी embedded इमेज ढूँढने के लिए HTML फ़ाइल में iVBOR के लिए grep किया है, तो यही वजह है कि यह फिंगरप्रिंट काम करता है। R Markdown रिपोर्ट्स, सिंगल-फ़ाइल डैशबोर्ड्स, और scrape किए गए पेज सब उसी शक्ल का इस्तेमाल करते हैं, और एक बनाना उसी पैटर्न से होता है: फ़ाइल को raw के रूप में पढ़िए, एन्कोड कीजिए, और MIME प्रीफिक्स के पीछे पेस्ट कीजिए। ख़र्च पहले वाली साइज़ गणित है, मूल से एक-तिहाई बड़ा, आपका HTML में हमेशा के लिए बैठा हुआ, इसलिए embedded इमेज पतली रखिए।
APIs और वेब रिक्वेस्ट
इस लेख-जोड़े की डिकोडिंग वाली तरफ़ उन APIs से मिलती है जो आपको Base64 सौंपते हैं; यह तरफ़ उन APIs से मिलती है जो उसे चाहती हैं। पैटर्न हर बार वही है: बाइट्स एन्कोड कीजिए, स्ट्रिंग को JSON बॉडी में रखिए, भेजिए। आधुनिक HTTP क्लाइंट httr2 है:
library(httr2)
req <- request("https://httpbin.org/post")
req <- req_body_json(req, list(file = packed, name = "man.txt"))
req
#> <httr2_request>
#> POST https://httpbin.org/post
#> Body: JSON data
res <- req_perform(req)
res$status_code
#> [1] 200
राह की तीन बातें। रिक्वेस्ट का कन्स्ट्रक्टर request() है, और httr2 की पहली रिलीज़ से ही यह उसी नाम से चल रहा है। रिस्पॉन्स बॉडी, जब आप रिस्पॉन्स पढ़ते हैं, raw वेक्टर के रूप में आती है, इसलिए parse करने से पहले rawToChar()। और API कोई डायलैक्ट बोल सकता है: कुछ को URL-सुरक्षित वर्णमाला चाहिए, कुछ को पैडिंग छीली हुई, और JSON API को स्ट्रिंग वैल्यू में = से कोई दिक्कत नहीं, इसलिए पैडिंग का सवाल तभी URL का सवाल बनता है जब Base64 path या query में यात्रा करे। संदेह में, spec अपने सिर में रखने के बजाय API के उदाहरण पढ़िए।
डेटाबेस, कॉन्फ़िग और एनवायरनमेंट
डेटाबेस में Base64 टेक्स्ट कॉलम से गुड़वाया गया बायनेरी है, और एन्कोड की तरफ है ब्लॉब को अंदर जाने से पहले पैक करना। यहाँ DBI और RSQLite के रास्ते SQLite पर राउंड ट्रिप है:
library(DBI)
library(RSQLite)
db <- dbConnect(SQLite(), ":memory:")
dbExecute(db, "CREATE TABLE files (name TEXT, payload TEXT)")
stored <- base64encode(charToRaw("stored in a database"))
dbExecute(db, paste0("INSERT INTO files VALUES ('note.txt', '", stored, "')"))
row <- dbGetQuery(db, "SELECT * FROM files")
rawToChar(base64decode(row$payload))
#> [1] "stored in a database"
विकल्प है बाइट्स को native रूप में BLOB के रूप में स्टोर करना, जिस स्थिति में Base64 की ज़रूरत ही नहीं होती और कॉलम R में raw वेक्टर के रूप में लौटता है। TEXT में Base64 वाला रूप पोर्टेबिलिटी के लिए मौजूद है: आप इसे एक टेक्स्ट एडिटर से देख सकते हैं, diff कर सकते हैं, और हर दूसरी भाषा इसे बिना बायनेरी ड्राइवर्स के पढ़ सकती है। वही तर्ज इसे कॉन्फ़िग में भी ले जाती है, जहाँ सर्टिफ़िकेट या secret YAML, JSON, या एनवायरनमेंट वेरिएबल में स्ट्रिंग के रूप में स्टोर होता है:
Sys.setenv("API_CERT" = base64encode(charToRaw("LTSSECRET")))
Sys.getenv("API_CERT")
#> [1] "TFRTU0VDUkVU"
यहाँ एक सावधानी का हक़ है, क्योंकि कॉन्फ़िग फ़ाइलें वही जगह हैं जहाँ secrets बसे रहते हैं: Base64 एन्कोडिंग है, एन्क्रिप्शन नहीं। कॉन्फ़िग फ़ाइल में Base64 वैल्यू को कोई भी पढ़ सकता है जो फ़ाइल पढ़ सकता है। वह transport और टेक्स्ट एडिटर्स में बच जाता है; वह कुछ भी संरक्षित नहीं करता।
ईमेल
ईमेल वही जगह है जहाँ 76-चर रैप का जन्म हुआ, और MIME एटैचमेंट आज भी उसे पहने घूमते हैं: Base64 कंटेंट, 76 चरों पर टूटा हुआ, पंक्तियों के बीच CRLF, एक part के अंदर जो Content-Transfer-Encoding: base64 घोषित करता है। वही shape बनाने वाला एन्कोडर base64encode() है, जिसकी रैपिंग आर्ग्यूमेंट्स MIME करार पर सेट हैं:
body <- "The quick brown fox jumps over the lazy dog, and then it came back down again."
mime_part <- base64encode(charToRaw(body), linewidth = 76, newline = "\r\n")
strsplit(mime_part, "\r\n", fixed = TRUE)[[1]]
#> [1] "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZywgYW5kIHRoZW4gaXQg"
#> [2] "Y2FtZSBiYWNrIGRvd24gYWdhaW4u"
R के पास first-class मेल क्लाइंट नहीं है, लेकिन वह बात तब भी खड़ी रहती है जब आप MIME parts हाथ से बनाते या जाँचते हैं, .eml फिक्स्चर बनाते हैं, या उनके अंदर से एटैचमेंट निकालते हैं: यह वह shape है जो Base64 को रखना होगा, और इस लेख-जोड़े की डिकोडिंग वाली तरफ़ दिखाती है कि दयालु डिकोडर वापसी में उसे अन-रैप कर रहे हैं।
बड़े पेलोड्स और स्ट्रिंग की छत
R स्ट्रिंग्स की एक सख़्त छत है, 2^31 - 1 बाइट्स, और क्योंकि एन्कोडेड रूप मूल की तुलना में करीब एक-तिहाई बड़ा होता है, करीब 1.5 GB के raw डेटा वाली फ़ाइल अपनी वन-लाइन एन्कोडिंग को उस दीवार के पार धकेल देगी। प्राक्टिकल उपाय वही है जो base64enc अपनी 2022 लॉन्ग वेक्टर रिलीज़ से दे रहा है: आउटपुट को एक स्ट्रिंग नहीं, पंक्तियों के रूप में रखिए:
big <- raw(10 * 1024 * 1024)
packed <- base64encode(big, linewidth = 76)
length(packed)
#> [1] 183961
sum(nchar(packed))
#> [1] 13981016
दस मेगाबाइट के ज़ीरो 183961 पंक्तियों बन जाते हैं, अधिकतम 76 चरों वाली, एक बिल्कुल साधारण वेक्टर जिसे आप पंक्ति-दर-पंक्ति लिख सकते हैं या पाइप के रास्ते stream कर सकते हैं, बिना कभी एक विशाल स्ट्रिंग को उठाया। data frame के केस के लिए, बायनेरी वैल्यूज़ की कॉलम, b64 स्पीड चैंपियन है: उसका Rust इंजन पूरी कॉलम को एक वेक्टराइज़्ड कॉल में एन्कोड कर देता है, जो पंक्ति-दर-पंक्ति लूप से बिल्कुल बड़ा अंतर है। अगर आपको एन्कोडेड वैल्यूज़ की कॉलम बनानी है, तो खुद एक तेज़ system.time() तुलना चलाइए; पंक्ति-दर-पंक्ति लूप और एक वेक्टराइज़्ड कॉल के बीच का फ़ासला आमतौर पर इतना बड़ा होता है कि यह मायने रखता है।
कमांड लाइन
हर काम को पूरी R सेशन की ज़रूरत नहीं होती। क्लासिक Unix औज़ार Base64 को नैटिव रूप में बोलता है, हर प्लेटफ़ॉर्म पर बिना किसी फ़्लैग के एन्कोड करता है, और डिकोड -d से Linux पर और -D से macOS और BSDs पर:
echo -n "Hello, world!" | base64
#> SGVsbG8sIHdvcmxkIQ==
echo -n "SGVsbG8sIHdvcmxkIQ==" | base64 -d
#> Hello, world!
पहली पंक्ति का -n देखिए: उसके बिना echo एक न्यूलाइन जोड़ देता है, और आउटपुट 13 की जगह 14 बाइट्स एन्कोड करता है, IQ== के बजाय o= पर ख़त्म होकर। GNU का base64 आपके लिए रैप भी कर देगा (-w 76), जो तब उपयोगी है जब आप MIME-मंज़र इनपुट की उम्मीद करने वाले कुछ में pipe कर रहे हों। और एक पंक्ति की Rscript वही काम कर देती है, आपके स्क्रिप्ट्स में इस्तेमाल होने वाले वही पैकेज के साथ:
Rscript -e 'library(base64enc); writeLines(base64encode(charToRaw("Man")))'
#> TWFu
तेज़ जाँचों और पाइप के लिए शेल इस्तेमाल कीजिए; जब रिज़ल्ट को data frame, फ़ाइल, या रिपोर्ट में बसना हो, तब R कीजिए। और कई मेगाबाइट की स्ट्रिंग्स को टर्मिनल में पेस्ट न कीजिए: command-line आर्ग्यूमेंट्स Base64 से बहुत पहले ARG_MAX पर टकरा जाते हैं, इसलिए बजाय में फ़ाइल के रास्ते पाइप कीजिए।
जानने लायक फँसाव
यहाँ वह छोटी लिस्ट है जिसमें बताया गया है कि एन्कोड की तरफ़ R डेवलपर्स को कैसे काटती है, और ये सारे एकोसिस्टम के अपने हैं, Base64 की सामान्य बातें नहीं:
- स्ट्रिंग एक फ़ाइलनेम है।
base64encode("Man")Man नाम की फ़ाइल खोलने की कोशिश करता है। वॉर्निंग फ़ाइल का नाम लेती है; एरर कहता है कि कनेक्शन फ़ेल हुआ।base64encode()के साथ पहलेcharToRaw()इस्तेमाल कीजिए। - ख़ाली इनपुट तीन तरह से एन्कोड होता है।
base64encode(raw(0))character(0)वापस करता है, जबकिb64::encode("")औरbase64url::base64_urlencode("")""वापस करते हैं। डाउनस्ट्रीम स्ट्रिंग कोड ज़ीरो-लंबाई वेक्टर की उम्मीद नहीं करता। - openssl रैप करता है और फिर एक भूत-पंक्ति जोड़ देता है।
linebreaks = TRUELF के साथ 64 पर ब्रेक करता है और एक ट्रेलिंग न्यूलाइन जोड़ता है जोstrsplit()चुपचाप गिरा देता है, इसलिए बेपरवाह लाइन काउंट और चर काउंट एक पंक्ति से असहमत रहते हैं। - रैपिंग एक करार है। MIME 76 है CRLF के साथ, PEM 64 है, JSON APIs आमतौर पर कुछ भी नहीं चाहतीं। वह shape चुनिए जो दूसरी तरफ़ उम्मीद करती है, क्योंकि एक डिकोडर जो एक रैप शैली सह ले, दूसरी को ठुकरा देगा।
- URL-सुरक्षित वर्णमाला दोनों सिरों पर मेल खानी चाहिए। URL-safe में एन्कोड
"----"स्टैंडर्ड डिकोडर में पहले डैश पर फ़ेल होता है, और पैडिंग तभी%3Dबन जाती है जब स्ट्रिंग URL के अंदर बसे। - b64_chunk चार के गुणांक माँगता है। कोई भी दूसरी चौड़ाई आपको
Chunk size must be a multiple of 4.दिलाती है, क्योंकि Base64 ग्रुप को आधा नहीं काटा जा सकता। - ट्रेलिंग न्यूलाइन डिकोडर को पैनिक करवा सकता है। जो फ़ाइलें
b64::decode_file()पढ़ने वाली हैं, उनका अंत न्यूलाइन के बिना होना चाहिए;cat(),writeLines()नहीं। - JWT के समय-claims लागू होते हैं।
jwt_decode_hmac()एक्सपायर्ड टोकन और भविष्य कीnbfclaims ठुकराता है, और अगर आपhttr2दूसरा लोड करें, तो वहjoseकेjwt_*फ़ंक्शनों पर छाया डाल देता है। - पेलोड दिखता है। JWT, data URI, या कॉन्फ़िग फ़ाइल में Base64 claims को कोई भी पढ़ सकता है। एन्कोडिंग एन्क्रिप्शन नहीं है।
- स्ट्रिंग की छत एक दीवार है, दिशा-निर्देश नहीं। प्रति R स्ट्रिंग करीब 1.5 GB raw डेटा वह जगह है जहाँ वन-लाइन एन्कोडिंग बसना बंद कर देती है; बड़ा आउटपुट पंक्तियों के रूप में रखिए या उसे stream कीजिए।
बेस्ट प्रैक्टिस
base64enc::base64encode()का इस्तेमाल करते समय एन्कोड से पहलेcharToRaw()से कन्वर्ट कीजिए। अगर सीधी चर इनपुट और वेक्टराइज़ेशन चाहिए, तोb64::encode()वह पैकेज है जो स्ट्रिंग को स्ट्रिंग की तरह समझता है।- रोज़मर्रा का काम
base64enc::base64encode()पर ही रखिए; जहाँ स्पीड, सच्चा वेक्टराइज़ेशन, या URL-सुरक्षित इंजन चाहिए, वहाँb64की ओर बढ़िए; और जहाँ PEM-मंज़र का आउटपुट चाहिए औरopensslपहले से प्रोजेक्ट में है, वहाँ वह इस्तेमाल कीजिए। - रैपिंग का चुनाव पैकेज के हिसाब से नहीं, चैनल के हिसाब से कीजिए: JSON और URLs के लिए कुछ भी नहीं, MIME के लिए CRLF के साथ 76, PEM-शैली ब्लॉक्स के लिए 64, और बात एकरूप रखिए ताकि पाइप की डिकोडिंग वाली तरफ़ यह जान सके कि क्या उम्मीद करे।
- जो कुछ URL, JWT, या फ़ाइलनेम में बसेगा, उसे बिना पैडिंग के URL-सुरक्षित वर्णमाला से एन्कोड कीजिए, और ईमेल के लिए स्टैंडर्ड वर्णमाला रखिए।
- JWT के लिए
exp(औरiat) स्पष्ट रूप से सेट कीजिए, टोकन पर भरोसा करने से पहलेjwt_decode_hmac()से वेरिफ़ाई कीजिए, और याद रखिए कि हर claim सार्वजनिक टेक्स्ट है। - अपने एन्कोड पाथ को राउंड ट्रिप से टेस्ट कीजिए,
identical(text, rawToChar(base64decode(base64encode(charToRaw(text))))); यह एक ही पंक्ति है और यह वर्णमाला, पैडिंग, और कैरेक्टर सेट की गलतियाँ एक साथ पकड़ लेती है। - फ़ाइलों के लिए पूरी फ़ाइल को एक स्ट्रिंग में उठाने के बजाय
b64::encode_file()याbase64::encode()की तरफ़ झुकीए, और जहाँ सख़्त डिकोडर पढ़ने वाला है, वहाँ आउटपुटcat()से लिखिए। - पैकिंग टेप को ईमानदार रखिए: Base64 बाइट्स को चलने देता है, उन्हें गुप्त नहीं बनाता और न ही छोटा; अगर लक्ष्य गुप्ता है तो पहले एन्क्रिप्शन, अगर साइज़ है तो पहले कंप्रेशन।
R में Base64 कैसे आया
यह फ़ॉर्मैट R के उससे जुड़ने से बहुत पहले आ चुका था। इसे 1987 में Privacy-Enhanced Mail प्रोटोकॉल के लिए मानकीकृत किया गया (RFC 989), 1993 में MIME ने इसे अपनाया (RFC 1521, फिर 1996 में अंतिम RFC 2045, जो आज भी 76-चर रैप की परिभाषा देता है), 2003 में RFC 3548 ने इसे साफ़-सुथरा किया, और 2006 में RFC 4648 ने उसकी आधुनिक शक्ल तय की, जिसने URL-सुरक्षित वर्णमाला, बिना-पैडिंग का विकल्प, और उसका छोटा भाई Base32 जोड़े। R की कहानी सितंबर 2012 में शुरू हुई, जब Simon Urbanek का base64enc CRAN पर उतरा और दस साल से ज़्यादा समय तक "इसे Base64 कैसे करें" के सवाल का खामोशी से डिफ़ॉलट जवाब बना रहा, 2015 में checkUTF8() और 2022 में लॉन्ग वेक्टर सपोर्ट पाता हुआ। क्रिप्टो की दुनिया openssl के रास्ते अंदर आई, Jeroen Ooms का सिस्टम OpenSSL के चारों ओर का लंबा-चला रैपर, जिसकी base64_encode() तब से PEM-मंज़र वाला विकल्प रही है। पुराना base64 पैकेज, यह भी Ooms का, अक्टूबर 2024 में खुल कर एक कंपैटिबिलिटी रैपर के रूप में दोबारा जारी हुआ, और अब उसका अपना वर्णन नई ऐप्लिकेशन को दूसरी जगह की ओर ले जाता है। फिर जनवरी 2024 में b64 आया, extendr से बना Rust इंजन, जिसने वेक्टराइज़ेशन और वर्णमालाओं की एक पूरी गिरी लाई, और अप्रैल 2026 में jose ने वर्ज़न 2.0 रिलीज़ किया, जिसने jwt_* API में ED25519 सपोर्ट जोड़ा और साइन किए हुए टोकन को first-class नागरिक बना दिया। नतीजा एक ऐसा टूलबॉक्स है जहाँ हर काम के लिए एक एन्कोडर है: रोज़मर्रा की स्ट्रिंग्स, PEM ब्लॉक्स, स्पीड, URLs, फ़ाइलें, और टोकन।
मज़े का कोना
क्योंकि एक पूरा गाइड मुस्कान पर ख़त्म होना चाहिए:
- इंटरनेट की हर Base64 PNG वही चरों से शुरू होती है: magic हेडर 89 50 4E 47 हमेशा
iVBORमें एन्कोड होता है, इसलिए HTML फ़ाइल में उस फिंगरप्रिंट के लिए grep करने से हर embedded इमेज मिल जाती है। फ़ॉर्मैट्स के फिंगरप्रिंट होते हैं, और यह एक प्रीफिक्स है। base64encode("Man")शब्द Man को एन्कोड नहीं करता। वह Man नाम की फ़ाइल ढूँढने निकल पड़ता है, चेतावनी देता है कि उसे नहीं खोल पा रहा, और हार मान लेता है। Base64 एकोसिस्टम की सबसे R-विशिष्ट फँसाव, जो आर्ग्यूमेंट लिस्ट में सबके सामने छुपी है।- OpenSSL का रैप किया हुआ स्ट्रिंग हमेशा ट्रेलिंग न्यूलाइन पर ख़त्म होता है, इसलिए PEM ब्लॉक की आख़िरी पंक्ति कभी फ़ाइल की आख़िरी पंक्ति नहीं होती। रैप में वाक्य के अंत में फुल-स्टॉप है, चाहे आप चाहें या न चाहें।
- ख़ाली स्ट्रिंग तीन अलग-अलग तरीकों से एन्कोड होती है:
base64encज़ीरो स्ट्रिंग्स का वेक्टर वापस करता है,b64औरbase64urlख़ाली स्ट्रिंग वापस करते हैं। R ख़ालीपन से मिलता है, और R तीन जवाब देता है। - jose JWT हेडर में
typकोalgसे पहले लिखता है, जबकि हाथ से लिखे उदाहरनों में ज़्यादातरalgपहले आता है। JSON को की-क्रम की परवाह नहीं, और JWT वेरिफ़िकेशन को पता है, लेकिन आपकी स्ट्रिंग diff को नहीं। - MIME की 76-चर सीमा 1993 का मेसेज-पंक्ति लंबाई का फैसला है, जो चार RFC के रास्ते हर उस ईमेल एटैचमेंट तक पहुँचा है जिसे आपने कभी भेजा है। वह रैप जो आप आज सेट कर रहे हैं, उस पर तर्क हो चुका था जब R के पास रंगीन प्लॉट तक नहीं था।
b64ऐसी वर्णमालाएँ भी डिकोड कर देगा जिन्हें आपने कभी नहीं देखा: BinHex, IMAP modified UTF-7, bcrypt, crypt, और URL-सुरक्षित जोड़ी, हर एक का अपना इंजन। वही Rust कोड जो आपके JSON फ़ील्ड को पैक करता है, वही 1980 के दशक की Macintosh एटैचमेंट को अनपैक कर सकता है।
सारांश
अपना एन्कोडर काम के हिसाब से चुनिए: रोज़मर्रा की स्ट्रिंग्स के लिए base64enc, जहाँ चैनल की शक्ल हो तो linewidth और newline के साथ; PEM-शैली 64-चर आउटपुट के लिए, या वह पहले से प्रोजेक्ट में हो, openssl; स्पीड, वेक्टर्स, या URL-सुरक्षित इंजनों के लिए b64; और फ़ाइल के कामों और URL-सुरक्षित स्ट्रिंग्स के लिए छोटे विशेषज्ञ base64 और base64url। base64enc::base64encode() से एन्कोड करने से पहले charToRaw() से कन्वर्ट कीजिए, रैपिंग चैनल के हिसाब से चुनिए पैकेज के हिसाब से नहीं, जो कुछ URL या JWT में बसेगा उसके लिए URL-सुरक्षित वर्णमाला रखिए, टोकन jose से साइन और वेरिफ़ाई कीजिए, और हर पाथ को राउंड ट्रिप से टेस्ट कीजिए। फ़ॉर्मैट खुद बिल्कुल दो जगहों पर क़रीब नहीं माफ़ करता, वर्णमाला और लाइन-ब्रेक्स, और एन्कोडर ज़्यादातर इसमें अलग हैं कि जब आप इनमें से एक ग़लत कर लेते हैं तो आपको वह बात कितनी ईमानदारी से बताते हैं। और जब दूसरी दिशा बुलाए, जब कोई स्ट्रिंग आती है और आपको उसे खोलना है, बाइट्स का मतलब जाँचना है, और चुपचाप फ़ेल होने वाले डिकोडर से बचना है, तो बहन साइट का संगत लेख R में Base64 डिकोडिंग की विस्तार से कहानी कहता है।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: R में Base64 डिकोडिंग: एक सम्पूर्ण गाइड