क्या सीएसवी प्रारूप को एक रेगेक्स द्वारा परिभाषित किया जा सकता है?


19

एक सहकर्मी और मैंने हाल ही में इस बात पर बहस की है कि क्या शुद्ध रेगेक्स सीएसवी प्रारूप को पूरी तरह से एनकैप्सलेट करने में सक्षम है, जैसे कि यह किसी भी दिए गए एस्केप चार, कोट चार और सेपरेटर चार के साथ सभी फाइलों को पार्स करने में सक्षम है।

रेगेक्स को निर्माण के बाद इन वर्णों को बदलने में सक्षम होने की आवश्यकता नहीं है, लेकिन इसे किसी अन्य किनारे के मामले में विफल नहीं होना चाहिए।

मैंने तर्क दिया है कि यह सिर्फ एक टोकन के लिए असंभव है। एकमात्र रेगेक्स जो ऐसा करने में सक्षम हो सकता है, एक बहुत ही जटिल पीसीआरई शैली है जो सिर्फ टोकन से आगे बढ़ती है।

मैं कुछ की तलाश में हूँ:

... सीएसवी प्रारूप एक संदर्भ मुक्त व्याकरण है और इस तरह, अकेले रेगेक्स के साथ पार्स करना असंभव है ...

या मैं गलत हूँ? क्या यह संभव है कि सीएसवी को सिर्फ एक पॉज़िक्स रेगेक्स से पार्स किया जाए?

उदाहरण के लिए, यदि एस्केप चार और कोट चार दोनों हैं ", तो ये दो लाइनें वैध सीएसवी हैं:

"""this is a test.""",""
"and he said,""What will be, will be."", to which I replied, ""Surely not!""","moving on to the next field here..."

यह CSV नहीं है क्योंकि कहीं भी कोई नेस्टिंग (IIRC) नहीं है
शाफ़्ट फ़्रीक

1
लेकिन किनारे के मामले क्या हैं? शायद CSV में और भी बहुत कुछ है, जितना मैंने कभी सोचा था?
c69

1
@ c69 कैसे बच और उद्धरण चार के बारे में हैं "। फिर निम्नलिखित मान्य है:"""this is a test.""",""
स्पेंसर रथबुन

क्या आपने यहां से regexp की कोशिश की ?
dasblinkenlight

1
आपको किनारे के मामलों के लिए बाहर देखने की ज़रूरत है, लेकिन एक रेगेक्स को सीएसवी को टोकन करने में सक्षम होना चाहिए जैसा कि आपने इसे वर्णित किया है। रेगेक्स को मनमाने ढंग से संख्याओं की गिनती करने की आवश्यकता नहीं है - इसे केवल 3 तक गिनने की आवश्यकता है, जो नियमित अभिव्यक्ति कर सकते हैं। जैसा कि दूसरों ने उल्लेख किया है, आपको एक अच्छी तरह से परिभाषित प्रतिनिधित्व को लिखने की कोशिश करनी चाहिए जो आप एक सीएसवी टोकन होने की उम्मीद करते हैं ...
17

जवाबों:


20

सिद्धांत में अच्छा है, व्यवहार में भयानक है

CSV द्वारा मैं आपको RFC 4180 में वर्णित कन्वेंशन का मतलब मानने जा रहा हूँ ।

बुनियादी सीएसवी डेटा का मिलान करना तुच्छ है:

"data", "more data"

नोट: BTW, यह एक .split ('/ n') का उपयोग करने के लिए बहुत अधिक कुशल है। इस तरह से बहुत सरल और अच्छी तरह से संरचित डेटा के लिए विभाजन ('' '') फंक्शन। नियमित एक्सप्रेशन NDFSM (गैर-नियतात्मक परिमित) के रूप में कार्य करता है। स्टेट मशीन) जो एक बार बर्बाद होने से बच जाती है, एक बार जब आप एग्जिट केस जोड़ना शुरू कर देते हैं जैसे कि बच के चर्चे।

उदाहरण के लिए यहाँ सबसे व्यापक नियमित अभिव्यक्ति मिलान स्ट्रिंग मैंने पाया है:

re_valid = r"""
# Validate a CSV string having single, double or un-quoted values.
^                                   # Anchor to start of string.
\s*                                 # Allow whitespace before value.
(?:                                 # Group for value alternatives.
  '[^'\\]*(?:\\[\S\s][^'\\]*)*'     # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*"     # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*    # or Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Allow whitespace after value.
(?:                                 # Zero or more additional values
  ,                                 # Values separated by a comma.
  \s*                               # Allow whitespace before value.
  (?:                               # Group for value alternatives.
    '[^'\\]*(?:\\[\S\s][^'\\]*)*'   # Either Single quoted string,
  | "[^"\\]*(?:\\[\S\s][^"\\]*)*"   # or Double quoted string,
  | [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*  # or Non-comma, non-quote stuff.
  )                                 # End group of value alternatives.
  \s*                               # Allow whitespace after value.
)*                                  # Zero or more additional values
$                                   # Anchor to end of string.
"""

यह यथोचित रूप से एकल और दोहरे उद्धरण चिह्नों को संभालता है, लेकिन मूल्यों में नयापन नहीं है, उद्धरणों से बच गया है, आदि।

स्रोत: स्टैक ओवरफ्लो - मैं जावास्क्रिप्ट के साथ एक स्ट्रिंग पार्स कैसे कर सकता हूं

एक बार यह एक दुःस्वप्न बन जाता है, जब आम किनारे के मामलों को पेश किया जाता है ...

"such as ""escaped""","data"
"values that contain /n newline chars",""
"escaped, commas, like",",these"
"un-delimited data like", this
"","empty values"
"empty trailing values",        // <- this is completely valid
                                // <- trailing newline, may or may not be included

अकेले न्यूलाइन-ए-वैल्यू एज केस वाइल्ड में पाए जाने वाले रेजेक्स आधारित पर्सर्स के 99.9999% को तोड़ने के लिए पर्याप्त है। एकमात्र 'वाजिब' विकल्प है RegEx का उपयोग बेसिक कंट्रोल / नॉन-कंट्रोल कैरेक्टर (यानी टर्मिनल बनाम नॉन-टर्मिनल) टोकन के लिए जो एक स्टेट मशीन के साथ उच्च स्तर के विश्लेषण के लिए उपयोग किया जाता है।

स्रोत: अनुभव अन्यथा व्यापक दर्द और पीड़ा के रूप में जाना जाता है।

मैं jquery-CSV का लेखक हूं , जो एकमात्र जावास्क्रिप्ट है, जो पूरी तरह से RFC- कम्प्लायंट, CSV पार्सर है। मैंने इस समस्या से निपटने में कई महीने बिताए हैं, कई बुद्धिमान लोगों के साथ बात कर रहे हैं, और एक टन की कोशिश कर रहे हैं, तो कोर पार्सर इंजन के 3 पूर्ण पुनर्लेखन सहित विभिन्न कार्यान्वयन।

tl; dr - कहानी का नैतिक, PCRE अकेले कुछ भी पार्स करने के लिए चूसता है लेकिन सबसे सरल और सख्त नियमित (Ie टाइप- III) व्याकरण। यद्यपि, यह टर्मिनल और गैर-टर्मिनल स्ट्रिंग को टोकन देने के लिए उपयोगी है।


1
हाँ, यह मेरा अनुभव भी रहा है। एक बहुत ही सरल CSV पैटर्न की तुलना में पूरी तरह से इनकैप्सुलेट करने का कोई भी प्रयास इन चीजों में चलता है, और फिर आप बड़े पैमाने पर रेगेक्स की दक्षता समस्याओं और जटिलता दोनों समस्याओं के खिलाफ तैयार हो जाते हैं। क्या आपने नोड-सीएसवी लाइब्रेरी को देखा है? इस सिद्धांत को भी मान्य किया गया है। प्रत्येक गैर तुच्छ कार्यान्वयन आंतरिक रूप से एक पार्सर का उपयोग करता है।
स्पेंसर रथबुन

@SpencerRathbun हां। मुझे यकीन है कि मैंने पहले नोड-सीएसवी स्रोत पर एक नज़र डाली है। यह प्रसंस्करण के लिए एक विशिष्ट चरित्र टोकन राज्य मशीन का उपयोग करता प्रतीत होता है। Jquery-csv पार्सर टर्मिनल / गैर-टर्मिनल टोकन के लिए regex का उपयोग करने के अलावा एक ही मूल अवधारणा पर काम करता है। चार-बाय-चार आधार पर मूल्यांकन और समवर्ती करने के बजाय, रेगेक्स एक समय में कई गैर-टर्मिनल वर्णों का मिलान करने और उन्हें एक समूह (यानी स्ट्रिंग) के रूप में वापस करने में सक्षम है। यह अनावश्यक संघनन को कम करता है और 'दक्षता में वृद्धि' करनी चाहिए।
इवान प्लाइस

20

रेगेक्स किसी भी नियमित भाषा को पार्स कर सकता है, और पुनरावर्ती व्याकरण जैसी फैंसी चीजों को पार्स नहीं कर सकता है। लेकिन CSV एक नियमित रूप से नियमित रूप से प्रतीत होता है, इसलिए रेगेक्स के साथ पारगम्य है।

चलो परिभाषा से काम करते हैं: अनुमति अनुक्रम, विकल्प रूप विकल्प ( |), और पुनरावृत्ति (क्लेन स्टार, *) हैं।

  • निर्विवाद मूल्य नियमित है: [^,]*# कोई भी चार लेकिन अल्पविराम
  • एक उद्धृत मूल्य नियमित है: "([^\"]|\\\\|\\")*"# किसी भी चीज़ का अनुक्रम लेकिन उद्धरण "या बची हुई बोली \"या बच निकले\\
    • कुछ रूपों में उद्धरण के साथ भागने वाले उद्धरण शामिल हो सकते हैं, जो ("")*"उपरोक्त अभिव्यक्ति के लिए एक संस्करण जोड़ता है ।
  • एक अनुमत मूल्य नियमित है: <unquoted-value> |<quoted-value>
  • एक एकल CSV लाइन नियमित है: <value> (,<value>)*
  • लाइनों को अलग करने का एक क्रम \nभी स्पष्ट रूप से नियमित है।

मैंने इनमें से प्रत्येक भाव का सावधानीपूर्वक परीक्षण नहीं किया, और न ही कभी पकड़े गए समूहों को परिभाषित किया। मैं भी कुछ तकनीकी चीजों पर भुला, वर्ण के वेरिएंट जिनमें से उपयोग किया जा सकता है जैसे ,, ", या पंक्ति विभाजक: इन नियमितता नहीं टूटते, तो आप सिर्फ कई थोड़ा अलग भाषाओं मिलता है।

यदि आप इस प्रमाण में कोई समस्या दे सकते हैं, तो कृपया टिप्पणी करें! :)

लेकिन इसके बावजूद, शुद्ध नियमित अभिव्यक्ति द्वारा CSV फ़ाइलों का व्यावहारिक पार्सिंग समस्याग्रस्त हो सकता है। आपको यह जानना होगा कि कौन-सा वेरिएंट पार्सर को खिलाया जा रहा है, और इसके लिए कोई मानक नहीं है। जब तक कोई व्यक्ति सफल नहीं हो जाता या आप किसी भी रूप में प्रारूप फ़ॉर्म टिप्पणियों को विभाजित नहीं करते, तब तक आप प्रत्येक पंक्ति के विरुद्ध कई पार्सर्स आज़मा सकते हैं। लेकिन इसे कुशलता से या बिल्कुल भी करने के लिए नियमित अभिव्यक्ति के अलावा अन्य साधनों की आवश्यकता हो सकती है।


4
व्यावहारिक बिंदु के लिए बिल्कुल एक +1। ऐसा कुछ है जो मुझे यकीन है, कहीं न कहीं एक गहरी (वंचित) मूल्य का एक उदाहरण है जो उद्धृत मूल्य संस्करण को तोड़ देगा मुझे अभी पता नहीं है कि यह क्या है। कई पार्सर्स के साथ 'मस्ती' होगी "ये दो काम, लेकिन अलग-अलग जवाब दें"

1
आपको स्पष्ट रूप से बैकस्लैश-एस्कैप्ड-कोट्स बनाम डबल्ड-कोट-एस्कैप्ड-कोट्स के लिए अलग-अलग रेगेक्स की आवश्यकता होगी। पूर्व प्रकार के सीएसवी क्षेत्र के लिए एक रेगेक्स कुछ ऐसा होना चाहिए [^,"]*|"(\\(\\|")|[^\\"])*", और बाद वाला कुछ ऐसा होना चाहिए [^,"]*|"(""|[^"])*"। (खबरदार, जैसा कि मैंने इनमें से किसी का भी परीक्षण नहीं किया है!)
आने वाली

किसी ऐसी चीज के लिए शिकार करना जो एक मानक हो सकता है, एक ऐसा मामला है जो छूट गया है - एक रिकॉर्ड डिलिमीटर के साथ एक मूल्य जो संलग्न है। यह व्यावहारिक को और भी मजेदार बना देता है जब इसे संभालने के कई अलग-अलग तरीके होते हैं

अच्छा जवाब है, लेकिन अगर मैं दौड़ता हूं perl -pi -e 's/"([^\"]|\\\\|\\")*"/yay/'और पाइप करता है "I have here an item,\" that is a test\""तो परिणाम `याय है जो एक परीक्षण \" "है। मिथिंक आपके रेगेक्स त्रुटिपूर्ण है।
स्पेंसर रथबुन

@SpencerRathbun: जब मेरे पास अधिक समय होगा तो मैं वास्तव में रेग्जेस का परीक्षण करूंगा और संभवत: कुछ प्रूफ-ऑफ-कॉन्सेप्ट कोड पेस्ट करूंगा जो टेस्ट पास करता है। क्षमा करें, कार्य दिवस चल रहा है।
9000

5

सरल उत्तर - शायद नहीं।

पहली समस्या एक मानक की कमी है। जबकि कोई अपने सीएसवी का वर्णन इस तरह से कर सकता है जिसे कड़ाई से परिभाषित किया गया है, कोई भी सीएसवी फाइलों को सख्ती से परिभाषित करने की उम्मीद नहीं कर सकता है। "आप जो करते हैं उसमें रूढ़िवादी रहें, जो आप दूसरों से स्वीकार करते हैं उसमें उदार रहें" -जॉन पोस्टल

यह मानते हुए कि किसी के पास एक मानक है जो स्वीकार्य है, भागने के पात्रों का सवाल है और अगर ये संतुलित होने की आवश्यकता है।

कई सीएसवी प्रारूपों में एक स्ट्रिंग के रूप में परिभाषित किया गया है string value 1,string value 2। हालाँकि, यदि उस स्ट्रिंग में अल्पविराम है तो वह अभी है "string, value 1",string value 2। यदि इसमें एक उद्धरण शामिल है तो यह बन जाता है "string, ""value 1""",string value 2

इस बिंदु पर मेरा मानना ​​है कि यह असंभव है। समस्या आपको यह निर्धारित करने की आवश्यकता है कि आपने कितने उद्धरण पढ़े हैं और यदि कोई कॉमा मूल्य के दोहरे उद्धृत मोड के अंदर या बाहर है। कोष्ठक को संतुलित करना एक असंभव रेगेक्स समस्या है। कुछ विस्तारित नियमित अभिव्यक्ति इंजन (PCRE) इससे निपट सकते हैं, लेकिन यह तब एक नियमित अभिव्यक्ति नहीं है।

आपको /programming/8629763/csv-parsing-with-a-context-free-grammar उपयोगी मिल सकता है ।


संशोधित:

मैं भागने के पात्रों के लिए प्रारूपों को देख रहा हूं और ऐसा कोई भी नहीं मिला है जिसकी मनमानी गिनती की आवश्यकता हो - ताकि यह समस्या न हो।

हालाँकि, भागने के चरित्र और रिकॉर्ड सीमांकक (के साथ शुरू करने के लिए) के मुद्दे हैं। http://www.csvreader.com/csv_format.php जंगली में विभिन्न स्वरूपों पर एक अच्छा पढ़ा है।

  • उद्धृत स्ट्रिंग के नियम (यदि यह एक एकल उद्धृत स्ट्रिंग या एक डबल उद्धृत स्ट्रिंग है) भिन्न हैं।
    • 'This, is a value' बनाम "This, is a value"
  • भागने के पात्रों के लिए नियम
    • "This ""is a value""" बनाम "This \"is a value\""
  • एम्बेडेड रिकॉर्ड सीमांकक की हैंडलिंग ({rd})
    • (कच्चा एम्बेड किया हुआ) "This {rd}is a value"बनाम (बच गया) "This \{rd}is a value"बनाम (अनुवादित)"This {0x1C}is a value"

यहां मुख्य बात यह है कि एक स्ट्रिंग होना संभव है जिसमें हमेशा कई मान्य व्याख्याएं होंगी।

संबंधित प्रश्न (एज मामलों के लिए) "क्या एक अवैध स्ट्रिंग होना संभव है जिसे स्वीकार किया गया है?"

मुझे अभी भी दृढ़ता से संदेह है कि एक नियमित अभिव्यक्ति है जो हर वैध CSV से मेल खा सकती है जो कुछ एप्लिकेशन द्वारा बनाई गई है और हर सीएसवी को अस्वीकार कर दिया है जिसे पार्स नहीं किया जा सकता है।


1
उद्धरण के अंदर उद्धरण संतुलित होने की आवश्यकता नहीं है। इसके बजाय, एक एम्बेडेड उद्धरण से पहले उद्धरण की एक समान संख्या होनी चाहिए, जो स्पष्ट रूप से नियमित है ("")*":। यदि मूल्य के अंदर के उद्धरण बंद हैं, तो यह पहले से ही हमारा व्यवसाय नहीं है।
9000

यह मेरी स्थिति है, अतीत में "डेटा ट्रांसफर" के लिए इन भयानक बहानों में। केवल एक चीज जो उन्हें ठीक से संभाला था वह एक पार्सर था, शुद्ध रेगेक्स हर कुछ हफ्तों में टूट गया।
स्पेंसर रथबुन

2

पहले अपने CSV के लिए व्याकरण को परिभाषित करें (क्या फ़ील्ड सीमांकक बच गए हैं या किसी तरह से एन्कोडेड हैं यदि वे पाठ में दिखाई देते हैं?) और फिर यह निर्धारित किया जा सकता है कि क्या यह regex के साथ पार्स है। व्याकरण पहले: पार्सर दूसरा: http://www.boyet.com/articles/csvparser.html यह ध्यान दिया जाना चाहिए कि यह विधि एक टोकन का उपयोग करती है - लेकिन मैं एक पॉज़िक्स रेगेक्स को कम नहीं कर सकता जो सभी किनारे के मामलों से मेल खाता हो। यदि आपका CSV प्रारूपों का उपयोग गैर-नियमित और संदर्भ मुक्त है ... तो आपका उत्तर आपके प्रश्न में है। यहाँ अच्छा अवलोकन: http://nikic.github.com/2012/06/15/The-true-power-of- अनियमित-express.html


2

यह rexxp सामान्य CSV को टोकन कर सकता है, जैसा कि RFC में वर्णित है:

/("(?:[^"]|"")*"|[^,"\n\r]*)(,|\r?\n|\r)/

स्पष्टीकरण:

  • ("(?:[^"]|"")*"|[^,"\n\r]*) - एक सीएसवी क्षेत्र, उद्धृत या नहीं
    • "(?:[^"]|"")*" - एक उद्धृत क्षेत्र;
      • [^"]|""- प्रत्येक चरित्र या तो नहीं है ", या के "रूप में बच गया""
    • [^,"\n\r]* - एक अयोग्य क्षेत्र, जिसमें शामिल नहीं हो सकता है , " \n \r
  • (,|\r?\n|\r)- निम्नलिखित विभाजक, या तो ,या एक नई रेखा
    • \r?\n|\r - एक नई पंक्ति, एक \r\n \n \r

इस regexp का बार-बार उपयोग करके संपूर्ण CSV फ़ाइल का मिलान और सत्यापन किया जा सकता है। तब उद्धृत फ़ील्ड को ठीक करना आवश्यक है, और इसे विभाजकों के आधार पर पंक्तियों में विभाजित करना है।

जावास्क्रिप्ट के आधार पर जावास्क्रिप्ट में CSV पार्सर के लिए कोड है:

var csv_tokens_rx = /("(?:[^"]|"")*"|[^,"\n\r]*)(,|\r?\n|\r)/y;
var csv_unescape_quote_rx = /""/g;
function csv_parse(s) {
    if (s && s.slice(-1) != '\n')
        s += '\n';
    var ok;
    var rows = [];
    var row = [];
    csv_tokens_rx.lastIndex = 0;
    while (true) {
        ok = csv_tokens_rx.lastIndex == s.length;
        var m = s.match(csv_tokens_rx);
        if (!m)
            break;
        var v = m[1], d = m[2];
        if (v[0] == '"') {
            v = v.slice(1, -1);
            v = v.replace(csv_unescape_quote_rx, '"');
        }
        if (d == ',' || v)
            row.push(v);
        if (d != ',') {
            rows.push(row)
            row = [];
        }
    }
    return ok ? rows : null;
}

क्या यह उत्तर आपके तर्क को तय करने में मदद करता है, यह आपको तय करना है; मैं बस एक छोटा, सरल और सही CSV पार्सर पाकर खुश हूं।

मेरी राय में एक lexकार्यक्रम कम या ज्यादा बड़ी नियमित अभिव्यक्ति है, और वे सी प्रोग्रामिंग भाषा जैसे बहुत अधिक जटिल स्वरूपों को टोकन दे सकते हैं।

RFC 4180 परिभाषाओं के संदर्भ में :

  1. लाइन ब्रेक (CRLF) - CRLF, LF या CR को अनुमति देते हुए regexp अधिक लचीला है।
  2. फ़ाइल में अंतिम रिकॉर्ड में एंडिंग लाइन ब्रेक नहीं हो सकता है - रेगेक्सपी क्योंकि इसे अंतिम लाइन ब्रेक की आवश्यकता होती है, लेकिन पार्सर इसके लिए समायोजित करता है।
  3. शायद एक वैकल्पिक हेडर लाइन है - यह पार्सर को प्रभावित नहीं करता है।
  4. प्रत्येक पंक्ति में संपूर्ण फ़ाइल में समान फ़ील्ड होनी चाहिए - लागू नहीं किए गए
    रिक्त स्थान को किसी फ़ील्ड का हिस्सा माना जाता है और इसे अनदेखा नहीं किया जाना चाहिए - ठीक
    है रिकॉर्ड में अंतिम फ़ील्ड का अल्पविराम द्वारा पालन नहीं किया जाना चाहिए - लागू नहीं किया गया
  5. प्रत्येक क्षेत्र डबल कोट्स में संलग्न हो भी सकता है और नहीं भी ... - ठीक है
  6. लाइन ब्रेक (CRLF), दोहरे उद्धरण और अल्पविराम वाले फ़ील्ड को दोहरे उद्धरणों में संलग्न किया जाना चाहिए - ठीक है
  7. एक मैदान के अंदर दिखाई देने वाली एक डबल-कोट को दूसरे डबल उद्धरण के साथ पूर्ववर्ती होने से बच जाना चाहिए - ठीक है

आरईजीएक्स 4180 आवश्यकताओं के अधिकांश रीजेक्सप ही संतुष्ट करता है। मैं दूसरों से सहमत नहीं हूं, लेकिन उन्हें लागू करने के लिए पार्सर को समायोजित करना आसान है।


1
यह पूछे गए प्रश्न को संबोधित करने की तुलना में आत्म-प्रचार की तरह दिखता है, देखें कि उत्तर कैसे दें
gnat

1
@gnat, मैंने अधिक स्पष्टीकरण देने के लिए, RFC 4180 के खिलाफ regexp की जांच करने और इसे कम आत्म-प्रचार करने के लिए अपने उत्तर को संपादित किया। मेरा मानना ​​है कि इस उत्तर का मूल्य है, क्योंकि इसमें एक परीक्षण किया गया rexxp है जो Excel और अन्य स्प्रेडशीट द्वारा उपयोग किए गए CSV के सबसे सामान्य रूप को टोकन कर सकता है। मुझे लगता है कि यह सवाल सुलझाता है। छोटा CSV पार्सर दर्शाता है कि इस regexp का उपयोग करके CSV को पार्स करना आसान है।
सैम वाटकिंस

अपने आप को अत्यधिक बढ़ावा देने की इच्छा के बिना, यहां मेरी पूरी छोटी सी सीएसवी और टीएसवी लाइब्रेरी हैं, जिन्हें मैं थोड़ा स्प्रेडशीट ऐप के हिस्से के रूप में उपयोग कर रहा हूं (Google शीट मेरे लिए बहुत भारी लगता है)। यह खुला स्रोत / सार्वजनिक डोमेन / CC0 कोड है जो मेरे द्वारा प्रकाशित सभी सामानों की तरह है। मुझे उम्मीद है कि यह किसी और के लिए उपयोगी हो सकता है। sam.aiki.info/code/js
सैम वाटकिंस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.