3 पॉइंट द्वारा GN⁺ 2024-03-21 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Python re में $, multiline mode बंद होने पर भी, सिर्फ string के अंत पर ही नहीं बल्कि अंत में मौजूद आखिरी newline के ठीक पहले भी match कर सकता है
  • सिर्फ इसलिए कि ^ “string की शुरुआत” जैसा दिखता है, यह मान लेना सही नहीं कि $ भी पूरी तरह symmetric तरीके से काम करेगा; इसका वास्तविक मतलब regex implementation के अनुसार बदलता है
  • "cat\n" पर $, \z, \Z के नतीजे PHP, ECMAScript, Python, Go, Java 8, .NET 7.0, Rust में एक-दूसरे से अलग हैं, और Python का \z Python 3.14 में नया जोड़ा गया है
  • अगर अंत का newline स्वीकार्य है, तो multiline mode का $ तालिका के सभी platforms पर "cat\n" से match करता है, लेकिन newline को छोड़कर सिर्फ अंत से match करना हो तो syntax का चुनाव बदल जाता है
  • अगर आखिरी newline से match नहीं करना है, तो ज़्यादातर platforms में \z का उपयोग किया जाता है, और Python 3.14 से पहले तथा ECMAScript में अलग-अलग विकल्पों पर विचार करना पड़ता है

Python re में $ कहाँ match करता है

  • Python regex module re में $, multiline mode बंद होने पर भी, string के अंत या string के अंत में मौजूद आखिरी newline के ठीक पहले match कर सकता है
  • cat$, "lolcat" से match होता है और "internet cat video" से match नहीं होता, इसलिए यह सरल लग सकता है, लेकिन "cat\n" जैसी trailing newline वाली string में नतीजा उम्मीद से अलग हो सकता है
  • re.MULTILINE देने पर $, string के अंत और हर line के अंत, यानी हर newline के ठीक पहले, match करता है
  • default behavior में भी $ string के अंत से match करता है, और अगर string के अंत में newline है तो उस newline के ठीक पहले भी match करता है

आखिरी newline को छोड़कर match करना

  • अगर सख्ती से सिर्फ string के अंत से match करना है, तो सिर्फ $ काफी नहीं हो सकता, और \z\Z end anchors के उम्मीदवार बनते हैं
  • Python regex documentation और दूसरे regex syntax विवरण के आधार पर, implementation के अनुसार \z और \Z का support और अर्थ अलग-अलग होता है
  • "cat\n" के लिए अंतर इस प्रकार है
    • PHP: "cat$" multiline हो या न हो, match करता है; "cat\z" match नहीं करता, और "cat\Z" match करता है
    • ECMAScript: multiline "cat$" match करता है, लेकिन non-multiline "cat$" match नहीं करता; \z और \Z supported नहीं हैं
    • Python: "cat$" multiline हो या न ho, match करता है; "cat\z" और "cat\Z", "cat\n" से match नहीं करते
    • Go और Rust: multiline "cat$" match करता है, लेकिन non-multiline "cat$" और "cat\z" match नहीं करते; \Z supported नहीं है
    • Java 8 और .NET 7.0: "cat$" multiline हो या न हो, match करता है; "cat\z" match नहीं करता, और "cat\Z" match करता है
  • Python का \z, Python 3.14 में नया जोड़ा गया, और इससे पहले के versions में supported नहीं था
  • अगर अंत का newline स्वीकार्य है, तो multiline mode का $ तालिका के सभी platforms में लगातार "cat\n" से match करता है
  • अगर अंत के newline से match नहीं करना है, तो ज़्यादातर platforms में \z का उपयोग करना चाहिए; Python 3.14 से पहले \Z, और ECMAScript में non-multiline $ का उपयोग करना होगा
  • तालिका का data regex101.com से लिया गया था, और इसे वास्तविक runtime में test नहीं किया गया था

1 टिप्पणियां

 
GN⁺ 2024-03-21
Hacker News टिप्पणियाँ
  • मैं पहले से ^ को “लाइन की शुरुआत” और $ को “लाइन का अंत” मानता आया हूँ
    regex के साथ काम करते समय टेक्स्ट को लाइन-दर-लाइन प्रोसेस करना बहुत आम है, इसलिए नतीजा अक्सर एक जैसा ही होता है, लेकिन इन ऑपरेटरों के बारे में सोचने का मेरा तरीका अब भी “string” से ज़्यादा “line” के करीब है
    शायद grep के जरिए regex सीखा, इसलिए input को string नहीं बल्कि line के रूप में देखने की आदत पड़ गई

    • मैंने भी शीर्षक देखकर सोचा, “बिल्कुल नहीं, ऐसी बात किसने कही?”
      लगभग 20 साल से regex इस्तेमाल कर रहा हूँ, लेकिन $ को string का अंत कहा जाता है, यह शायद पहली बार सुन रहा हूँ; मैं इसे हमेशा line के अंत के रूप में ही सोचता रहा हूँ
    • लेख में ^ को “string की शुरुआत” कहना खटकता है
      असल में जैसे $ “line का अंत” है, वैसे ही ^ भी “लाइन की शुरुआत” है, और string की शुरुआत \A, string का अंत \Z के ज़्यादा करीब लगता है
    • मैं भी ऐसा ही सोचता था, लेकिन Perl में खुद आज़माकर देखा तो $ डिफ़ॉल्ट रूप से string के अंत के लिए एक positive lookahead assertion की तरह काम करता है
      यह newline character को match करके consume नहीं करता
      केवल multiline mode में newline की जगह पर match होता है, लेकिन तब भी शायद उसे consume नहीं करता
      वास्तव में $ का इस्तेमाल करके ऐसा regex नहीं बना पाया जो एक line के आख़िरी character को capture करे, फिर newline को consume करे और अगली line के पहले character को capture करे; capture group बस $ पर ही खत्म हो जाता है
    • मेरे अंदर यह समझ grep से ज़्यादा Vim ने बैठाई
  • POSIX regex और Python regex अलग हैं
    सामान्य तौर पर regex syntax सार्वभौमिक नहीं होती, इसलिए जिस implementation का इस्तेमाल कर रहे हों, उसके docs देखने चाहिए
    POSIX अध्याय 9 के अनुसार regex strings पर काम करते हैं, लेकिन कुछ utilities processing को line-based तक सीमित करती हैं
    साथ ही, $ को उस anchor के रूप में बताया गया है जो match की जा रही string के अंत पर स्थिर रहता है, इसलिए आखिर $ string के अंत का मतलब देगा या line के अंत का, यह utility या mode तय करता है
    grep, sed, awk, Python जैसे आम tools डिफ़ॉल्ट रूप से line-based काम करते हैं, इसलिए आमतौर पर इसे line के अंत के रूप में लिया जाता है
    एक ही सार्वभौमिक regex syntax जैसी कोई चीज़ नहीं है
    जब तक यह न पता हो कि कौन-सी language और कौन-से options इस्तेमाल हो रहे हैं, regex को भरोसेमंद ढंग से पढ़ना या लिखना संभव नहीं
    https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1...

  • इस विषय पर Robert Elder से अपरिचित लोगों को उनसे परिचित कराने का यह बढ़िया मौका है
    वह YouTube और अपने ब्लॉग पर अच्छा content बनाते हैं, और regex series में अलग-अलग tools द्वारा implement किए गए regex behavior के अंतर को काफ़ी गहराई से समझाते हैं
    हाल का वीडियो भी अच्छा है: https://www.youtube.com/watch?v=ys7yUyyQA-Y
    HN पाठकों की रुचि के लायक content भी बहुत है, और consulting की वास्तविकता व मुश्किलों जैसे विषय भी कवर करते हैं
    https://www.youtube.com/@RobertElderSoftware
    https://blog.robertelder.org/
    https://blog.robertelder.org/regular-expressions/
    https://www.youtube.com/watch?v=cK87ktENPrI

  • जब मैंने Perl सीखा, तब regex उन पहली चीज़ों में से थी जो सच में मेरे हाथ में बैठ गईं, और आज भी “Camel” किताब की वजह से Perl दिल के एक कोने में आराम से बसा है
    अब सबसे अहम समझ यह है कि हर implementation अलग होती है, इसलिए जब भी कुछ काम करना होता है, मैं उस implementation की reference sheet निकालकर देखता हूँ
    उदाहरण के लिए Emacs regex में \w जैसे word character का support नहीं है और \s_- जैसी character class इस्तेमाल करनी पड़ती है, जो परेशान करती है, लेकिन documentation और discoverability के मामले में Emacs मुझे सबसे बेहतर लगता है
    कुछ utilities में parentheses को escape करना पड़ता है और कुछ में नहीं, और कहीं यह behavior configurable होता है, कहीं नहीं
    उलझन, झुंझलाहट और इनकार—सब चरणों से गुजर चुका हूँ, अब बस इसे स्वीकार कर लिया है
    concept हर जगह वही रहता है, लेकिन dialect बदल जाती है

    • मेरा दिमाग Perl regex में सोचता है, फिर जिस language में काम कर रहा हूँ उसके असंगत हिस्सों के हिसाब से उसे translate करता है
      खासकर shell में, sed/grep/awk GNU हैं या BSD, यह याद रखने से कहीं ज़्यादा मैं pipeline में perl जोड़ देना पसंद करता हूँ
    • जानना चाहूँगा कि यह किसी के हाथ में इतनी अच्छी तरह कैसे बैठी
      Perl तो ऐसा दिखता है जैसे बिल्ली कीबोर्ड पर चल गई हो
  • ऐसा लगता है जैसे अनगिनत ख़राब hiring managers अब “regex में string के अंत को कैसे match करेंगे?” को अपनी trap question सूची में जोड़ रहे हों

  • regex से जुड़ी सूची में Perl को छोड़ना अजीब है
    perlre docs में $ को यूँ समझाया गया है: यह string के अंत से match करता है, या string के अंत में मौजूद newline से पहले match करता है, या /m के साथ किसी भी newline से पहले match करता है

    • regex से सबसे मज़बूती से जुड़ी language कही जाने वाली Perl को छोड़ देना काफ़ी बड़ी चूक लगता है
      शायद इसका मतलब यह भी है कि आजकल Perl लोगों के ध्यान से काफ़ी बाहर हो गई है
  • Raku, जिसे पहले Perl 6 कहा जाता था, में ^ और $ को string की शुरुआत/अंत के लिए तय किया गया, और ^^$$ को line की शुरुआत/अंत के लिए जोड़ा गया
    इसमें multiline mode नहीं है, और उसकी ज़रूरत भी नहीं पड़ती
    \h horizontal whitespace के लिए है, और \v vertical whitespace के लिए
    इसे पूरी तरह दोबारा सोचकर और फिर से लिखने की वजह से, पुराने behavior से लोगों को जो हैरानी होती थी उससे सीखने का फ़ायदा मिला

    • इसलिए यह ज़िद्दी इंसान Perl 6 इस्तेमाल नहीं कर सकता
      ऐसा लगता है जैसे दशकों में सीखी हुई line noise जैसी syntax को यूँ ही मिलाकर रख दिया गया हो
      साफ़ तौर पर default इसका उल्टा होना चाहिए था
      ^ और $ को line के लिए, और ^^$$ को string के लिए इस्तेमाल करना ज़्यादा स्वाभाविक लगता
      क्योंकि यह ^^line1$\n^line2$\n^line3$\n$ जैसा दिखता है
      ऊपर से Perl 6 हर जगह नहीं है, लेकिन Perl 5 हर जगह है
    • अगर मैं होता, तो बिल्कुल उल्टा चुनता
      ^^, ^ से ज़्यादा “शुरुआत-जैसा” दिखता है
    • मैंने जो regex लिखे हैं उनमें लगभग सभी string की शुरुआत/अंत को मानकर लिखे गए थे
      आम तौर पर processing के लिए regex में line डाली जाती है, इसलिए पूरे string के लिए single ^ और $ चुनना कुछ हद तक backward compatibility बनाए रखता है
  • मुझे नहीं लगता कि regex को standardized मानने वाले बहुत लोग होंगे
    हर नए environment में जाने पर इसे फिर से सीखना पड़ा है

    • एक समय ऐसा था जब मुझे लगता था कि मैं सभी dialects जानता हूँ
      और भी regex dialects होंगे, लेकिन उनसे सामना नहीं होता, और जो मुझे पता हैं उनसे ज़्यादातर काम हो जाता है
      यह rental car चलाने जैसा है
      वह मेरी अपनी car से थोड़ा अलग चलती है, कुछ features नहीं होते और कुछ extra होते हैं, लेकिन कुल मिलाकर ज़्यादातर चीज़ें काफ़ी मिलती-जुलती हैं
    • ISO/IEC 14882 C++ standard library छह तरह की de facto court-standard regex syntaxes के implementation की मांग करती है: IEEE Std 1003.1-2008, यानी POSIX के BRE, ERE, awk, grep, egrep, और ECMA-262 EcmaScript 3
      इसलिए कम से कम मैं तो regex को कई सार्वजनिक आधिकारिक standards में standardized मानता हूँ
      https://open-std.org/jtc1/sc22/…
      https://pubs.opengroup.org/onlinepubs/9699919799/…
      https://262.ecma-international.org/14.0/…
    • मेरी जानकारी में बड़े समूह POSIX, Perl/PCRE, और Go की तरफ़ इस्तेमाल होने वाला RE2 हैं
      JavaScript सहित कई systems ने PCRE implement किया, क्योंकि Perl ने POSIX ढाँचे में कई उपयोगी extensions जोड़े
      मुझे याद था कि RE2 का उद्देश्य मौजूदा systems की performance समस्याओं और अजीब behavior को सीमित करना था, और मैं समझता था कि यह पूरा का पूरा Go में implement किया गया था
      बाद में पता चला कि RE2, Go से पहले आया था
    • Perl के बाद आई भाषाएँ आम तौर पर Perl regex syntax के किसी variant का उपयोग करती हैं, लेकिन हमेशा छोटे-मोटे फ़र्क होते हैं
      फिर भी $ का अर्थ और multiline mode में बदलने का तरीका आम तौर पर काफ़ी consistent रहता है
    • दिलचस्प बात यह है कि RFC 9485 https://datatracker.ietf.org/doc/rfc9485/ “I-Regexp: An Interoperable Regular Expression Format” पिछले साल अक्टूबर में ही प्रकाशित हुआ था
  • लोग string और line को गड़बड़ा रहे हैं
    string, characters का एक sequence है, और line को दो तरीकों से देखा जा सकता है
    अगर newline को line terminator मानें, तो line शून्य या अधिक non-newline characters के बाद newline लगने से बनती है, और अगर अंत में newline नहीं है तो वह complete line नहीं है
    POSIX यही नज़रिया अपनाता है
    अगर newline को line separator मानें, तो line शून्य या अधिक non-newline characters का sequence है
    किसी भी स्थिति में, line का content newline से पहले समाप्त होता है
    ^ और $ की semantics, चाहे single-line mode हो या multiline mode, line-based हैं
    string-based semantics में, या फ़ाइलों के मामले में कहें तो whole-file semantics में, \A और \Z या उनके equivalent का इस्तेमाल करना चाहिए
    दोनों व्याख्याओं के अपने फ़ायदे हैं
    serial connection से text भेजते समय newline को line terminator मानने पर यह जानना आसान होता है कि complete line मिली या नहीं
    text files में newline को line separator मानना सुविधाजनक हो सकता है क्योंकि तब आख़िरी line invalid state में नहीं जाती, लेकिन line terminator रखने पर अधूरी लिखी गई line को पहचानना संभव होता है

  • इसी वजह से Ruby-based apps में कई बार गंभीर bugs आए हैं
    हमेशा \A\z का इस्तेमाल करना चाहिए
    https://homakov.blogspot.com/2012/05/saferweb-injects-in-var...
    https://sakurity.com/blog/2015/02/28/openuri.html
    https://sakurity.com/blog/2015/06/04/mongo_ruby_regexp.html