Saya punya beberapa data TSV
ID Name Email
1 test [email protected]
321 stan [email protected]
Saya ingin menguraikan ini ke dalam daftar hash
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
Saya mengalami masalah dengan menggunakan metacharacter baris baru untuk membatasi baris header dari baris nilai. Definisi tata bahasa saya:
use v6;
grammar Parser {
token TOP { <headerRow><valueRow>+ }
token headerRow { [\s*<header>]+\n }
token header { \S+ }
token valueRow { [\s*<value>]+\n? }
token value { \S+ }
}
my $dat = q:to/EOF/;
ID Name Email
1 test [email protected]
321 stan [email protected]
EOF
say Parser.parse($dat);
Tapi ini kembali Nil
. Saya pikir saya salah memahami sesuatu yang mendasar tentang regex di raku.
Nil
. Sejauh ini umpan baliknya cukup mandul, bukan? Untuk debugging, unduh komaide jika Anda belum melakukannya, dan / atau lihat Bagaimana pelaporan kesalahan dalam tata bahasa ditingkatkan? . Anda punyaNil
karena pola Anda diasumsikan mundur semantik. Lihat jawaban saya tentang itu. Saya sarankan Anda menghindari mundur. Lihat jawaban @ user0721090601 tentang itu. Untuk kepraktisan dan kecepatan, lihat jawaban JJ. Juga, Pengantar jawaban umum untuk "Saya ingin mengurai X dengan Raku. Adakah yang bisa membantu?" .Jawaban:
Mungkin hal utama yang membuangnya adalah yang
\s
cocok dengan ruang horizontal dan vertikal. Untuk mencocokkan ruang horizontal saja, gunakan\h
, dan untuk mencocokkan ruang vertikal saja\v
,.Satu rekomendasi kecil yang saya buat adalah untuk menghindari memasukkan baris baru dalam token. Anda mungkin juga ingin menggunakan operator alternatif
%
atau%%
, karena mereka dirancang untuk menangani pekerjaan jenis ini:Hasil dari
Parser.parse($dat)
ini adalah sebagai berikut:yang menunjukkan kepada kita bahwa tata bahasa telah berhasil mengurai segalanya. Namun, mari kita fokus pada bagian kedua dari pertanyaan Anda, yang Anda inginkan tersedia dalam variabel untuk Anda. Untuk melakukan itu, Anda harus menyediakan kelas tindakan yang sangat sederhana untuk proyek ini. Anda cukup membuat kelas yang metodenya cocok dengan metode tata bahasa Anda (meskipun yang sangat sederhana, seperti
value
/header
yang tidak memerlukan pemrosesan khusus selain pengetatan, dapat diabaikan). Ada beberapa cara yang lebih kreatif / ringkas untuk menangani pemrosesan milik Anda, tetapi saya akan menggunakan pendekatan ilustrasi yang cukup sederhana. Inilah kelas kami:Setiap metode memiliki tanda tangan
($/)
yang merupakan variabel pencocokan regex. Jadi sekarang, mari kita bertanya informasi apa yang kita inginkan dari setiap token. Di baris tajuk, kami ingin setiap nilai tajuk, berturut-turut. Begitu:Setiap tanda dengan quantifier di atasnya akan diperlakukan sebagai
Positional
, sehingga kami juga bisa mengakses setiap pertandingan sundulan individu dengan$<header>[0]
,$<header>[1]
, dll Tapi mereka adalah pertandingan benda, jadi kami hanya cepat stringify mereka. Themake
perintah memungkinkan token lain untuk mengakses data khusus yang kami buat.Baris nilai kami akan terlihat identik, karena
$<value>
token adalah hal yang kami pedulikan.Ketika kita sampai pada metode terakhir, kita akan ingin membuat array dengan hash.
Di sini Anda dapat melihat bagaimana kami mengakses hal-hal yang kami proses
headerRow()
danvalueRow()
: Anda menggunakan.made
metode ini. Karena ada beberapa valueRows, untuk mendapatkan masing-masingmade
nilainya, kita perlu melakukan peta (ini adalah situasi di mana saya cenderung menulis tata bahasa saya hanya<header><data>
di tata bahasa, dan mendefinisikan data sebagai beberapa baris, tetapi ini adalah cukup sederhana itu tidak terlalu buruk).Sekarang kita memiliki header dan baris dalam dua array, itu hanya masalah membuat mereka menjadi array hash, yang kita lakukan dalam
for
loop. Theflat @x Z @y
just interolates elemen, dan penugasan hash Melakukan Apa Yang Kami Maksud, tetapi ada cara lain untuk mendapatkan array dalam hash yang Anda inginkan.Setelah selesai, Anda baru saja
make
melakukannya, dan kemudian akan tersedia dimade
bagian parse:Cukup umum untuk membungkus ini menjadi suatu metode, seperti
Dengan begitu Anda bisa mengatakannya
sumber
class Actions { has @!header; method headerRow ($/) { @!header = @<header>.map(~*); make @!header.List; }; method valueRow ($/) {make (@!header Z=> @<value>.map: ~*).Map}; method TOP ($/) { make @<valueRow>.map(*.made).List }
Anda tentu harus instantiate dulu:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
dan hanya memiliki valueRow tambahkan entri secara langsung sehingga Anda berakhir dengan adilmethod TOP ($!) { make %!entries }
. Tapi bagaimanapun juga ini adalah Raku dan TIMTOWTDI :-)<valueRow>+ %% \n
(Tangkap baris yang dibatasi oleh baris baru), tetapi mengikuti logika itu,<.ws>* %% <header>
akan menjadi "tangkap opsional spasi putih yang dibatasi oleh non-spasi putih ". Apakah saya melewatkan sesuatu?<.ws>
menangkap (<ws>
akan). OP mencatat bahwa format TSV dapat dimulai dengan spasi kosong opsional. Pada kenyataannya, ini mungkin akan lebih baik didefinisikan dengan token spasi-garis didefinisikan sebagai\h*\n\h*
, yang akan memungkinkan nilaiRow didefinisikan secara lebih logis sebagai<header> % <.ws>
%
/%%
diistilahkan sebagai "pergantian" op sebelumnya. Tapi itu nama yang tepat. (Padahal untuk menggunakannya|
,||
dan sepupu selalu menganggapku aneh.). Saya tidak pernah memikirkan teknik "mundur" ini sebelumnya. Tapi itu adalah ungkapan yang bagus untuk menulis regex yang cocok dengan pola yang diulang dengan beberapa pernyataan pemisah tidak hanya antara kecocokan pola tetapi juga memungkinkannya di kedua ujungnya (menggunakan%%
), atau di awal tetapi tidak berakhir (menggunakan%
), sebagai, eh, alternatif untuk di akhir tetapi tidak memulai logikarule
dan:s
. Bagus. :)TL; DR: Anda tidak. Cukup gunakan
Text::CSV
, yang mampu menangani setiap format.Saya akan menunjukkan berapa umur yang
Text::CSV
mungkin berguna:Bagian kuncinya di sini adalah data munging yang mengubah file awal menjadi array atau array (in
@data
). Namun itu hanya diperlukan, karenacsv
perintah tidak dapat menangani string; jika data ada dalam file, Anda dapat melakukannya.Baris terakhir akan mencetak:
Bidang ID akan menjadi kunci hash, dan semuanya array hash.
sumber
TL; DR
regex
backtrack.token
tidak. Itu sebabnya pola Anda tidak cocok. Jawaban ini berfokus pada menjelaskan hal itu, dan bagaimana memperbaiki tata bahasa Anda secara sepele. Namun, Anda mungkin harus menulis ulang, atau menggunakan parser yang sudah ada, yang pasti harus Anda lakukan jika Anda hanya ingin mengurai TSV daripada belajar tentang raku regex.Kesalahpahaman mendasar?
(Jika Anda sudah tahu istilah "regex" adalah yang sangat ambigu, pertimbangkan untuk melewatkan bagian ini.)
Satu hal mendasar yang mungkin Anda salah pahami adalah arti kata "regex". Berikut adalah beberapa arti populer yang diasumsikan oleh rakyat:
Ekspresi reguler formal.
Perl regex.
Perl Regular Regular Expressions (PCRE).
Ekspresi pencocokan pola teks yang disebut "regex" yang terlihat seperti di atas dan melakukan sesuatu yang serupa.
Tak satu pun dari makna ini yang kompatibel satu sama lain.
Sementara Perl regex secara semantik merupakan superset dari ekspresi reguler formal, mereka jauh lebih berguna dalam banyak hal tetapi juga lebih rentan terhadap pengulangan patologis .
Sementara Perl Kompatibel Regular Expressions kompatibel dengan Perl dalam arti mereka awalnya sama dengan Perl regex standar pada akhir 1990-an, dan dalam arti Perl mendukung mesin regex pluggable termasuk mesin PCRE, sintaks PCRE regex tidak identik dengan standar. Perl regex digunakan secara default oleh Perl pada tahun 2020.
Dan sementara ekspresi pencocokan pola teks yang disebut "regex" umumnya memang terlihat agak mirip satu sama lain, dan melakukan semua teks yang cocok, ada puluhan, mungkin ratusan, variasi dalam sintaksis, dan bahkan dalam semantik untuk sintaksis yang sama.
Ekspresi pencocokan pola teks Raku biasanya disebut "aturan" atau "regex". Penggunaan istilah "regex" menyampaikan fakta bahwa mereka terlihat agak seperti regex lain (meskipun sintaks telah dibersihkan). Istilah "aturan" menyampaikan fakta bahwa mereka adalah bagian dari serangkaian fitur dan alat yang jauh lebih luas yang meningkatkan hingga penguraian (dan seterusnya).
Perbaikan cepat
Dengan keluarnya aspek fundamental dari kata "regex", saya sekarang dapat beralih ke aspek mendasar dari perilaku "regex" Anda .
Jika kami mengalihkan tiga pola dalam tata bahasa Anda untuk
token
deklarator keregex
deklarator, tata bahasa Anda berfungsi seperti yang Anda inginkan:Satu-satunya perbedaan antara a
token
dan aregex
adalahregex
backtracks sedangkan atoken
tidak. Jadi:Selama pengolahan dari pola terakhir (yang bisa dan sering disebut "regex", tetapi yang sebenarnya deklarator adalah
token
, tidakregex
), yang\S
akan menelan'b'
, hanya karena sementara akan dilakukan selama pemrosesan dari regex di garis sebelumnya. Tapi, karena polanya dinyatakan sebagai atoken
, mesin aturan (alias "mesin regex") tidak mundur , sehingga kecocokan keseluruhan gagal.Itulah yang terjadi di OP Anda.
Perbaikan yang tepat
Solusi yang lebih baik secara umum adalah menyapih diri Anda dari asumsi perilaku backtracking, karena itu bisa lambat dan bahkan sangat lambat (tidak dapat dibedakan dari program yang digantung) ketika digunakan dalam pencocokan dengan string yang dibuat dengan berbahaya atau yang dengan kombinasi karakter yang tidak disengaja secara kebetulan.
Terkadang
regex
tepat. Misalnya, jika Anda menulis satu kali dan regex melakukan pekerjaan, maka Anda selesai. Tidak apa-apa. Itulah bagian dari alasan bahwa/ ... /
sintaks dalam raku menyatakan pola backtracking, sama sepertiregex
. (Kemudian lagi Anda dapat menulis/ :r ... /
jika Anda ingin mengaktifkan ratcheting - "ratchet" berarti kebalikan dari "mundur", jadi alihkan:r
regex ketoken
semantik.)Kadang-kadang mundur masih memiliki peran dalam konteks parsing. Sebagai contoh, walaupun tata bahasa untuk raku umumnya menghindari backtracking, dan sebaliknya memiliki ratusan
rule
s dantoken
s, namun tetap memiliki 3regex
s.Saya telah memutakhirkan jawaban @ user0721090601 ++ karena ini berguna. Ini juga membahas beberapa hal yang menurut saya secara idiomatis mematikan kode Anda, dan, yang terpenting, melekat pada
token
s. Mungkin itu jawaban yang Anda inginkan, yang akan keren.sumber