Orada tarafından popüler bir alıntı Jamie Zawinski :
Bazı insanlar, bir sorunla karşılaştığında, "Biliyorum, düzenli ifadeler kullanacağım" deyin. Şimdi onların iki problemi var.
Bu teklifin nasıl anlaşılması gerekiyor?
Orada tarafından popüler bir alıntı Jamie Zawinski :
Bazı insanlar, bir sorunla karşılaştığında, "Biliyorum, düzenli ifadeler kullanacağım" deyin. Şimdi onların iki problemi var.
Bu teklifin nasıl anlaşılması gerekiyor?
Yanıtlar:
Bazı programlama teknolojileri genellikle programcılar tarafından iyi anlaşılmamaktadır ( düzenli ifadeler , kayan nokta , Perl , AWK , IoC ... ve diğerleri ).
Bunlar, doğru problemleri çözmek için inanılmaz güçlü araçlar olabilir. Özellikle normal ifadeler normal dilleri eşleştirmek için çok kullanışlıdır. Ve sorunun özü de var: çok az insan düzenli bir dili nasıl tanımlayacağını bilmez (komik sembolleri kullanan bilgisayar bilimleri teorisi / dilbiliminin bir parçasıdır - Chomsky hiyerarşisinde okuyabilirsiniz ).
Bu şeylerle uğraşırken, eğer yanlış kullanırsanız, asıl sorununuzu çözmüş olmanız pek mümkün değildir. Bir kullanarak HTML maç için düzenli ifade (bir çok yaygın bir durum) Eğer anlamına gelecektir olacak sınır durumları özledim. Ve şimdi, hala çözemediğiniz orijinal bir probleminiz var ve çevresinde dolanan başka bir ince böcek de yanlış çözümü kullanarak ortaya çıkıyor.
Bu, düzenli ifadelerin kullanılmaması gerektiği anlamına gelmez, bunun yerine, ne tür sorunları çözebileceklerini ve çözemediklerini ve usluca kullanamadıklarını anlamak için çalışmak gerekir.
Yazılımı korumanın anahtarı, bakım kodu yazmaktır. Düzenli ifadeler kullanmak bu hedefe karşı olabilir. Düzenli ifadelerle çalışırken, özel bir etki alanına özgü dilde bir mini bilgisayar (özellikle belirleyici olmayan sonlu durum otomatları ) yazdınız . 'Merhaba dünyayı' eşdeğerini bu dilde yazmak kolaydır ve ona temel bir güven kazandırır; normal ifadenin içinde bulunduğu programın bir parçası değillerdir).
Şimdi yeni bir probleminiz var; Bunu çözmek için normal ifadenin aracını seçtiniz (uygun olmadığında) ve her ikisini de bulmak daha zor olan iki hataya sahipsiniz, çünkü başka bir soyutlama katmanında saklanmışlar.
Düzenli ifadelerin - özellikle önemsiz ifadelerin - kodlanması, anlaşılması ve sürdürülmesi potansiyel olarak zordur. Sadece [regex]
, sorgulayan kişinin problemlerinin cevabının bir regex olduğunu ve daha sonra sıkışıp kaldığını varsaydığı etiketli Stack Overflow etiketli soru sayısına bakmak zorundasınız. Birçok durumda sorun farklı bir şekilde çözülebilir (ve belki de gerekir).
Bunun anlamı, eğer bir regex kullanmaya karar verirseniz, şimdi iki probleminiz var:
Temel olarak, sanırım sorununuzu çözmenin başka bir yolu yoksa, yalnızca bir regex kullanmanız gerektiği anlamına gelir. Başka bir çözüm muhtemelen kodlaması, bakımı ve desteklenmesi daha kolay olacaktır. Daha yavaş veya daha az verimli olabilir, ancak eğer kritik değilse, bakım ve destek kolaylığı en önemli husus olmalıdır.
Çoğunlukla bir yanak dili şakasıdır, hakikaten de olsa.
Düzenli ifadelerin mükemmel bir uyum sağladığı bazı görevler vardır. Bir keresinde, 500 satırlık elle yazılmış özyinelemeli ayrıştırıcı kodunu tamamen hata ayıklamak için yaklaşık 10 dakika süren düzenli bir ifadeyle değiştirdim. İnsanlar regex'lerin anlaşılmasının ve hata ayıklanmasının zor olduğunu, ancak uygun şekilde uygulananların büyük bir el tasarımcısı gibi hata ayıklamak kadar zor olmadığını söylüyor. Benim örneğimde, regex olmayan çözümün tüm son vakalarını hata ayıklamak iki hafta sürdü.
Bununla birlikte, Ben Amca'nın ifadesini kullanmak:
Büyük bir verimlilik ile büyük sorumluluk gelir.
Başka bir deyişle, regex'ler, dilinize etkililik katar, ancak programcıya verilen görev için en okunabilir ifade modunu seçme konusunda daha fazla sorumluluk getirir.
Bazı şeyler başlangıçta normal ifadeler için iyi bir iş gibi görünür, ancak değildir. Örneğin, HTML gibi iç içe jetonlu herhangi bir şey. Bazen insanlar daha basit bir yöntem daha açık olduğunda düzenli ifade kullanırlar. Örneğin string.endsWith("ing")
, eşdeğer regex'ten daha kolay anlaşılır. Bazen insanlar büyük bir sorunu tek bir regex'e sıkıştırmaya çalışırlar; Bazen insanlar uygun soyutlamalar oluşturamazlar, aynı işi yapmak için iyi adlandırılmış bir işlev oluşturmak yerine tekrar tekrar bir regex tekrarlayın (belki de bir regex ile dahili olarak uygulanır).
Bazı nedenlerden dolayı regex'ler, tek bir sorumluluk ve DRY gibi normal yazılım mühendisliği prensiplerine kör bir nokta oluşturma eğiliminde. Bu yüzden onları seven insanlar bile zaman zaman problemli buluyorlar.
Jeff Atwood bir blog yazısında bu alıntıyı tartışan farklı bir yorum getiriyor: Düzenli İfadeler: Şimdi İki Sorunuz Var ( link için Euphoric sayesinde )
Jamie'nin yayınlarının tam metnini 1997'deki orijinal başlıkta inceleyerek aşağıdakileri bulduk:
Perl'in doğası, neredeyse diğer tüm tekniklerin hariç tutulması için düzenli ifadelerin kullanılmasını teşvik eder; A noktasından B noktasına ulaşmanın en açık ve en açık yolu (en azından daha iyisini bilmeyen insanlar için).
İlk teklif ciddiye alınamayacak kadar glib. Ama buna tamamen katılıyorum. İşte Jamie'nin yapmaya çalıştığı nokta şudur: normal ifadelerin kendi başına kötü olmadığı, ancak normal ifadelerin aşırı kullanılması kötüdür.
Eğer bile yok tamamen normal ifadeler anlamak içine çalıştırmak Altın Çekiç daha kolay ve (ayrıca bkz düzenli kodu ile aynı şeyi yapmak için daha net olurdu zaman düzenli ifadeler, bir sorunu çözmek için çalışıyor, sorunun CodingHorror: Regex kullanımı Regex kötüye vs ).
Alıntı içeriğine bakan ve Atwood'dan daha fazla ayrıntıya giren başka bir blog yazısı daha var: Jeffrey Friedl'in Blogu: Ünlü “Şimdi iki problemin var” alıntı
Bu alıntı ile devam eden birkaç şey var.
Alıntı önceki bir şaka bir yeniden ifade geçerli:
Bir problemle karşılaştığında, bazı insanlar "Hadi AWK kullanalım" diyor. Şimdi onların iki problemi var. - D. Tilbrook
Bu bir şaka ve gerçek bir kazı, ama aynı zamanda regex'i diğer kötü çözümlerle ilişkilendirerek kötü bir çözüm olarak vurgulamanın bir yolu. Çok ciddi bir an.
Bana göre - aklınızdan çıkarmayın, bu alıntı bilerek açıklamaya açık - anlam açıktır. Basitçe, düzenli bir ifade kullanma fikrini duyurmak problemi çözmedi. Ek olarak, kullandığınız dilden farklı olan kurallara ek bir dil ekleyerek, kodun bilişsel karmaşıklığını arttırdınız.
Şaka kadar komik olsa da, regex olmayan bir çözümün karmaşıklığını regex çözümünün karmaşıklığıyla + regex içeren ek karmaşıklıkla karşılaştırmanız gerekir. Ek regex ekleme maliyetine rağmen, regex ile ilgili bir sorunu çözmek faydalı olabilir.
RegularExpressionsarenoworsetoreadormaintainthananyotherunformattedcontent; indeedaregexisprobablyeasiertoreadthanthispieceoftexthere-butunfortunatelytheyhaveabadreputationbecausesomeimplementationsdon'tallowformattingandpeopleingeneraldon'tknowthatyoucandoit.
(Düzenli İfadeler, biçimlendirilmemiş diğer herhangi bir içerikten okumak veya bakım yapmaktan daha kötü değildir; aslında, bir regex'in bu metinden daha kolay okunması daha kolaydır - ancak ne yazık ki, kötü bir üne sahiptir, çünkü bazı uygulamalar genel olarak biçimlendirmelere ve insanlara izin vermez Yapabileceğini bilmiyorum.)
İşte önemsiz bir örnek:
^(?:[^,]*+,){21}[^,]*+$
Zaten okumak ya da sürdürmek o kadar zor değil, ama böyle göründüğünde daha da kolay:
(?x) # enables comments, so this whole block can be used in a regex.
^ # start of string
(?: # start non-capturing group
[^,]*+ # as many non-commas as possible, but none required
, # a comma
) # end non-capturing group
{21} # 21 of previous entity (i.e. the group)
[^,]*+ # as many non-commas as possible, but none required
$ # end of string
Bu $
, başa çıkmış bir örnek (yorum yapmaktan başka bir şey i++
değil) ama açıkça okumak, anlamak ve sürdürmekle ilgili hiçbir sorun olmamalı.
Düzenli ifadelerin ne zaman uygun olduğu ve ne zaman kötü bir fikir olduğu konusunda net olduğunuz sürece, onlarla ilgili yanlış bir şey yoktur ve çoğu zaman JWZ teklifi gerçekten geçerli olmaz.
*+
? Bunun (işlevsel olarak) adil olandan farkı *
nedir?
*+
Bu durumda tam anlamıyla bir anlam ifade etmiyor ; her şey sabitlenir ve 22'ye kadar sayılabilecek bir otomat tarafından tek bir geçişte eşleştirilebilir. Bu virgül olmayan kümelerdeki doğru değiştirici sadece eskidir *
. (Dahası, burada açgözlü ve açgözlü olmayan eşleştirme algoritmaları arasında da bir fark olmamalıdır. Bu son derece basit bir durumdur.)
ChrisF’in cevabına ek olarak - normal ifadelerin “kodlanması, anlaşılması ve sürdürülmesi zor”, daha da kötüsü: insanları HTML gibi, yapamadıkları şeyleri ayrıştırmak için kullanmaya kandırmaya yetecek kadar güçlüler. SO ile ilgili çok sayıda soruya bakın "HTML'yi nasıl ayrıştırırım?" Örneğin, tüm SO'daki en destansı cevap !
Düzenli ifadeler çok güçlüdür, ancak bir küçük ve büyük bir sorunu vardır; yazmak zor ve okumak imkansız.
En iyi durumda, normal ifadenin kullanılması sorunu çözer, bu nedenle yalnızca karmaşık kodun bakım sorununu yaşarsınız. Düzenli ifadeyi tam anlamıyla anlamazsanız, hem orijinal hem de çalışmayan okunamayan kodla ilgili bir sorun yaşarsınız.
Bazen normal ifadelere salt okunur kod denir. Düzeltme gerektiren düzenli bir ifadeyle karşılaşıldığında, sıfırdan başlamak ifadeyi anlamaya çalışmaktan daha hızlıdır.
Sorun şu ki regex karmaşık bir canavardır ve sadece regex'i mükemmel kullanırsanız sorununuzu çözersiniz. Aksi takdirde, 2 sorunla karşılaşırsınız: asıl sorununuz ve regex.
Yüz satır kod işini yapabileceğini iddia ediyorsunuz, ancak 100 satır açık, özlü kodun bir regex satırından daha iyi olduğu iddiasını da yapabilirsiniz.
Bunun bir kanıtına ihtiyacınız varsa: Bu SO Classic'i kontrol edebilir veya sadece SO Regex Tag'ı kullanarak taraklayabilirsiniz.
Anlamı iki bölümden oluşur:
2014'te istediğin gibi, bugünün bağlamına kıyasla 1997 bağlamı ideolojilerini programlama dillerine odaklamak ilginç olurdu. Bu tartışmaya buraya girmeyeceğim ancak Perl ve Perl hakkındaki görüşler büyük ölçüde değişti.
Bununla birlikte, 2013 bağlamında kalmak için ( bir coulé sous les ponts depuis) de, Jamie Zawinski'nin doğrudan bir alıntı olan ünlü bir XKCD çizgi romanını kullanarak alıntılarda canlandırma üzerine odaklanmayı öneririm :
Öncelikle ben Zawinski alıntı bir referans, çünkü bu komik anlamak için sorunları vardı ve bir Jay-Z şarkı sözleri bir alıntı ve GNU bir referans program --help -z
bayrağı 2 bana bunu anlamak için çok fazla kültürünü oldu, bu yüzden.
Eğlenceli olduğunu biliyordum, hissediyordum ama nedenini bilmiyordum. İnsanlar genellikle Perl ve regex'ler hakkında şakalar yapıyorlar, özellikle de en akıllı programlama dili olmadığı için, neden eğlenceli olması gerektiğini bilmiyorlar ... Belki de Perl satıcıları saçma şeyler yapıyor .
Dolayısıyla ilk alıntı, acı veren araçların programlanmasından kaynaklanan gerçek hayat sorunlarına (acı?) Dayalı alaycı bir şaka gibi görünüyor. Tıpkı bir çekiçin masonlara zarar verebilmesi gibi, geliştiricinin zarar verebilecekse (beyin, duygular) seçeceği araçlarla programlama. Bazen, hangi aracın en iyisi olduğu konusunda büyük tartışmalar olur, ancak neredeyse değersizdir, çünkü bu sizin zevkinize ya da programlama ekibinizin zevkine , kültürel ya da ekonomik nedenlerine göre bir problemdir . Bu konuda bir başka mükemmel XKCD komik:
İnsanların regex'ler hakkında acı duyduklarını anlayabiliyorum ve regex'lerin ne için tasarlandıkları için başka bir aracın daha uygun olduğuna inanıyorlar. @ Karl-bielefeldt sorunuzu büyük bir verimlilikle yanıtladığından büyük sorumluluk alır ve regex'ler özellikle bundan endişe duyuyor. Eğer bir geliştirici, regex'lerle nasıl ilgilendiğini umursamıyorsa, sonunda şifreyi koruyacak insanlar için bir acı olacaktır.
Damian Conway'ın Perl En İyi Uygulamaları (2005) kitabından tipik bir örnek gösteren bir alıntı ile alıntıların yeniden canlandırılması hakkındaki bu cevabı bitireceğim .
Bunun gibi bir kalıp yazdığını şöyle açıklıyor :
m{'[^\\']*(?:\\.[^\\']*)*'}
... böyle bir program yazmaktan daha fazla kabul edilebilir değildir :
sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;
Ama yeniden yazılabilir , hala güzel değil, ama en azından şu anda hayatta kalabilir.
# Match a single-quoted string efficiently...
m{ ' # an opening single quote
[^\\']* # any non-special chars (i.e., not backslash or single quote)
(?: # then all of...`
\\ . # any explicitly backslashed char
[^\\']* # followed by any non-special chars
)* # ...repeated zero or more times
' # a closing single quote
}x
Bu tür bir dikdörtgen biçimli kod , açık, sürdürülebilir ve okunabilir bir şekilde biçimlendirilebilen regex'lerin değil, ikinci problemdir.
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
Bilgisayar bilimlerinden öğrenmeniz gereken bir şey varsa, bu Chomsky hiyerarşisidir . Düzenli ifadelerle ilgili tüm sorunların bağlamsız gramer ayrıştırma girişimlerinden kaynaklandığını söyleyebilirim. CFG'de yuva düzeylerine bir sınır getirebilir (veya bir sınır getirebileceğinizi düşünebilirsiniz), bu uzun ve karmaşık düzenli ifadeleri alırsınız.
Düzenli ifadeler, tokenizasyon için tam ölçekli ayrıştırmaya göre daha uygundur.
Ancak, programcıların ayrıştırması gereken şaşırtıcı derecede büyük şeyler kümesi düzenli bir dille ayrıştırılabilir (veya daha da kötüsü, normal bir dille neredeyse ayrıştırılabilir ve sadece biraz daha fazla kod yazarsanız ...).
Eğer biri "aha, metni parçalara ayırmam gerekiyor, normal bir ifade kullanacağım" diyene alışkın olursa, aşağıdan aşağıya bir otomat, CFG çözümleyici veya daha da güçlü gramerler. Bu genellikle gözyaşlarıyla biter.
Bu nedenle, teklifin çok fazla çarpıtma olmadığını düşünüyorum, onların kullanımları var (ve çok kullanılmışlar, gerçekten çok faydalılar), ancak regexps'lere aşırı güveniyorlar (ya da özellikle eleştirel olmayan tercihleri) .
jwz bu alıntı ile sadece rocker kapalı. Düzenli ifadeler, herhangi bir dil özelliğinden farklı değildir - vidalanması kolay, kullanımı zor, zaman zaman güçlü, zaman zaman uygunsuz, sık sık iyi belgelenmiş, genellikle yararlı.
aynı şey, kayan nokta aritmetiği, kapanışlar, nesne yönelimi, asenkron I / O ya da adlandırabileceğiniz herhangi bir şey için de söylenebilir. Ne yaptığınızı bilmiyorsanız, programlama dilleri sizi üzebilir.
Eğer regexlerin okunmasının zor olduğunu düşünüyorsanız, söz konusu kalıbı tüketmek için eşdeğer çözümleyici uygulamasını okumayı deneyin. sık sık regexes kazanır çünkü tam ayrıştırıcılardan daha küçüktürler ve çoğu dilde de daha hızlıdırlar.
Kendini tanıtıcı bir blog yazıcısının nitelenmemiş ifadeler yaptığı için normal ifadeleri (veya başka bir dil özelliğini) kullanmaktan kaçının. kendiniz için bir şeyler deneyin ve sizin için neyin işe yaradığını görün.
Buna en sevdiğim, derinlemesine cevabım, dahili bir Google kod yorumundan çoğaltılan bir blog yazısında ünlü Rob Pike tarafından verildi: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html
Özet, onların kötü olmadıkları değil , fakat özellikle bazı şeyleri karıştırmak ve ayrıştırmak söz konusu olduğunda mutlaka uygun olmadıkları için görevler için sıkça kullanılıyorlar.
Düzenli ifadeler yazmak zordur, iyi yazmak zordur ve diğer teknolojilere göre daha pahalı olabilir ... Diğer taraftan, Lexers, doğru yazması oldukça kolaydır (kompakt olmasa da) ve test etmesi çok kolaydır. Alfanümerik tanımlayıcıları bulmayı düşünün. Regexp ("[a-ZA-Z _] [a-ZA-Z_0-9] *" gibi bir şey) yazmak çok zor değil, ama basit bir döngü olarak yazmak gerçekten zor değil. Bununla birlikte, döngünün performansı çok daha yüksek olacaktır ve kapakların altında daha az kod bulunacaktır. Düzenli bir ifade kütüphanesi büyük bir şeydir. Birini tanımlayıcıları ayrıştırmak için kullanmak, depoya süt almak için bir Ferrari kullanmak gibidir.
Bundan daha fazlasını, düzenli ifadelerin örneğin metin editörlerinde tek kullanımlık desen eşleştirmesinde yararlı olduğunu, ancak derlenmiş kodda nadiren kullanılması gerektiğini savunarak çok daha fazla olduğunu söylüyor. Okunmaya değer.
Bu Alan Perlis'in 34 numaralı epigramı ile ilgilidir:
Dize keskin bir veri yapısıdır ve geçtiği her yerde işlemin çoğaltılması söz konusudur. Bilgi gizlemek için mükemmel bir araçtır.
Bu nedenle, karakter dizginizi veri yapınız olarak seçtiyseniz (ve doğal olarak, onu işlemek için kullanılan algoritmalar olarak regex tabanlı kod), çalışıyor olsa bile bir sorunla karşılaşırsınız: uygun olmayan verilerin gösterimi için kötü tasarım uzatmak ve verimsiz.
Ancak, çoğu zaman işe yaramıyor: orijinal problem çözülmedi ve bu durumda iki probleminiz var.
Regex'ler hızlı ve kirli metin ayrıştırma için yaygın olarak kullanılır. Yalnızca düz bir dize eşleşmesinden biraz daha karmaşık olan desenleri ifade etmek için harika bir araçtır.
Ancak regex'ler karmaşıklaştıkça hizmet sorunları başa çıkıyor.
Bu nedenle, bir metin işleme sorunuyla başlamak, ona düzenli ifadeler uygulamak ve iki sorunla sonuçlanmak çok kolaydır, çözmeye çalıştığınız ve çözmeye çalıştığınız normal ifadelerle uğraşmakta olduğunuz asıl sorun (ancak doğru şekilde çözülmez) asıl sorun.