Garis log de-interleave: MODE KERAS
Menghadapi beberapa aturan yang disesalkan yang mengubah aslinya menjadi tugas penyortiran yang dimuliakan, saya memposting varian yang lebih menantang. Berteriak kepada Luis Mendo untuk saran tentang bagaimana meningkatkan tantangan asli.
Anda mewarisi server yang menjalankan beberapa aplikasi yang semuanya menghasilkan log yang sama.
Tugas Anda adalah untuk memisahkan baris dari file log berdasarkan sumber. Untungnya bagi Anda, orang yang menulis semua aplikasi cukup baik untuk meninggalkan tag yang menunjukkan sumbernya.
Log
Setiap baris akan terlihat seperti ini:
[app_name] Something horrible happened!
- Tag aplikasi selalu berada di antara tanda kurung siku dan hanya akan berisi karakter alfanumerik dan garis bawah.
- Tag aplikasi tidak kosong
- Mungkin nanti ada tanda kurung siku lain pada baris tertentu, tetapi tidak ada yang akan membentuk tag yang valid.
- Akan selalu ada setidaknya satu karakter non-spasi setelah tag.
- Log secara keseluruhan mungkin kosong.
- Tidak ada batasan berapa banyak tag aplikasi unik yang akan ada di file.
Dalam beberapa kasus, tag aplikasi mungkin hilang. Jika demikian, baris log adalah milik aplikasi yang paling baru dicatat.
- Baris pertama log akan selalu dimulai dengan tag aplikasi
- Sebuah baris yang diawali dengan
[belum tentu diberi tag. Jika ada karakter yang tidak valid di antara tanda kurung siku awal atau tidak], maka baris tersebut tidak diberi tag. - Tidak ada baris kosong yang muncul di log
Output yang Diharapkan
Anda harus mengeluarkan beberapa log yang benar-benar terpisah dengan tag aplikasi dihapus dari setiap baris log tempat mereka berada. Anda tidak perlu mempertahankan spasi di depan pada baris log mana pun.
Log keluaran harus dalam semacam pemetaan nilai kunci atau padanan yang wajar. Daftar format keluaran valid yang tidak lengkap:
- File yang diberi nama setelah tag aplikasi untuk setiap aplikasi
- Anda mungkin berasumsi bahwa file keluaran belum ada di direktori keluaran dalam kasus ini.
- Dictionary / map / hash / apa saja yang menggunakan tag aplikasi sebagai kunci dan string baris log yang dipisahkan baris baru sebagai nilai.
- String gabungan panjang yang dipisahkan oleh baris kosong dan diawali dengan tag aplikasi
- Daftar daftar [key, value]
- String JSON dengan tag aplikasi sebagai kunci dan array baris log sebagai nilai
- Dokumen penurunan harga dengan tag aplikasi sebagai tajuk dan
#di depan baris apa pun yang lolos dengan garis miring terbalik. - Fungsi Javascript yang mengambil string sebagai input dan output log terkait sebagai string yang dipisahkan baris baru.
Pada dasarnya, jika Anda tidak dapat mengetahui dari aplikasi mana baris log tersebut berasal, hasilnya tidak valid.
Contoh
Seluruh log mungkin terlihat seperti ini:
[weather] Current temp: 83F
[barkeep] Fish enters bar
Fish orders beer
[stockmarket] PI +3.14
[PI announced merger with E]
[barkeep] Fish leaves bar
[weather] 40% chance of rain detected
[ I have a lovely bunch of coconuts
Yang seharusnya menghasilkan tiga log berbeda:
cuaca:
Current temp: 83F
40% chance of rain detected
[ I have a lovely bunch of coconuts
pelayan bar:
Fish enters bar
Fish orders beer
Fish leaves bar
pasar saham:
PI +3.14
[PI announced merger with E]
Anda tidak akan diberi nama tag aplikasi sebelumnya. Anda harus menentukannya hanya dengan menganalisis file log.
Aturan dan Penilaian
- Ini adalah kode-golf , jadi kode terpendek menang.
- Aturan dan celah standar berlaku
- Gunakan format IO yang nyaman, asalkan setiap baris masukan direpresentasikan sebagai string, bukan tag + pesan yang telah diuraikan sebelumnya. Parsing adalah bagian dari tantangan ini .
- Baris log keluaran untuk setiap aplikasi harus muncul dalam urutan yang sama seperti di log asli.
- Anda dapat berasumsi bahwa log masukan hanya berisi karakter ASCII.
Jawaban
Python 3.8 , 95 byte
import re
lambda x:[((t:=re.match(r'\[(\w*)\]',s)or t)[1],s.split(t[0])[-1].strip())for s in x]
(Contoh TIO yang diperluas dengan input)
Penjelasan:
Python 3.8 diperlukan untuk :=operator. Ini mengambil daftar string sebagai input, dan mengeluarkan daftar (tag, body)tupel. Pertama, ini menggunakan pencocokan Regex untuk mendapatkan tag:
t:=re.match(r'\[(\w*)\]',s)or t)
Ini cocok dengan urutan awal karakter kata apa pun (alfanumerik + garis bawah) yang diapit dalam tanda kurung siku, dengan kata-kata sebagai grup penangkap. Jika string cocok dengan regex ini, takan menjadi matchobjek dengan dua elemen: pencocokan penuh dan grup. Misalnya, jika string adalah [tag] body, matchakan memiliki elemen [tag]dan tag.
Jika string tidak cocok dengan regex ini, maka re.match()mengembalikan None. Kode menjadi t = None or t, yang adil t = t, sehingga tag mempertahankan nilainya dari baris sebelumnya. Jika baris pertama tidak ada yang cocok, ini akan menyebabkan kesalahan, tapi kita tidak perlu khawatir tentang itu!
Kode tersebut kemudian membangun tupel t[1], s.split(t[0])[-1].strip(), di mana t[1]adalah grup penangkap (tag tanpa tanda kurung siku) dan t[0]merupakan tag dengan tanda kurung siku. Memisahkan string pada tag lengkap akan mengisolasi tubuh, apakah tag benar-benar ada dalam string atau tidak.
Retina 0.8.2 , 95 byte
+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1
O$`(\w+).*
$1
¶
¶¶
rm`(?<=^\1.*¶)¶(.\w+].)
(?<=(^|¶¶).\w+]).
¶
Cobalah secara online! Penjelasan:
+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1
Tandai semua baris yang tidak diberi tag.
O$`(\w+).*
$1
Urutkan garis, diambil dari jawaban saya untuk tantangan asli.
¶
¶¶
Beri spasi ganda pada garis.
rm`(?<=^\1.*¶)¶(.\w+].)
Hapus tag duplikat dan baris kosong di depannya. Ini berarti bahwa baris kosong yang tersisa adalah yang membatasi tag terpisah.
(?<=(^|¶¶).\w+]).
¶
Pindahkan tag ke barisnya sendiri.
perl -Fitur = katakan -n, 47 46 byte
(Disimpan satu byte milik @Dom Hastings)
$;=$1 if s/^\[(\w+)\] +//;$;{$;}.=$_}{say for%;
Bagaimana cara kerjanya?
Pertama, efek -nsakelar. Hal ini menyebabkan Perl membungkus program dalam satu putaran, yang membaca masukan dan mengeksekusi badan untuk setiap baris. Tetapi melakukannya dengan cara yang sangat sederhana, ia membungkus tubuh dalam loop sebelum melakukan penguraian apa pun, sebagai berikut:
perl -ne 'TEXT'
diubah menjadi
LINE: while (defined($_ = readline ARGV)) {
TEXT;
}
Tetapi itu berarti jika Anda TEXTadalah formulir LOOP_BODY}{FINAL_STATEMENT, Anda berakhir dengan program:
LINE: while (defined($_ = readline ARGV)) {
LOOP_BODY
}
{
FINAL_STATEMENT;
}
Kami menggunakan trik ini hanya untuk menyimpan beberapa byte dalam satu ENDblok.
Dalam program itu sendiri, kami menggunakan dua variabel untuk melakukan pembukuan kami. $;akan berisi tag saat ini, dan dalam hash %;, kami melacak baris untuk setiap tag. Sekarang, untuk setiap baris masukan, kami memeriksa untuk melihat apakah itu dimulai dengan tag, dan jika demikian, kami menghapusnya dari baris, dan mengingat tag:
$; = $1 if # Remember the tag if,
s/^\[(\w+)\] +//; # we can strip of a tag
Kami kemudian menggabungkan baris saat ini (dilucuti dari tag, jika ada) ke kumpulan baris yang sudah dikumpulkan untuk tag itu - jika tidak ada baris seperti itu, kami secara efektif menggabungkannya dengan string kosong:
$;{$;}.=$_ # Remember the current line
Akhirnya, setelah membaca semua baris, kami mencetak hash. Perl dengan mudah meratakan hash ke daftar sederhana jika Anda memperlakukannya sebagai daftar, mengganti kunci dan nilai. Ini memberi kita keluaran di mana setiap bagian dipisahkan oleh baris baru, dan dipimpin oleh tag.
say for%; # Print the flattened hash
05AB1E , 22 byte
vyD']¡н¦DžjÃÊi‚»]).¡#н
Masukan sebagai daftar baris, keluaran sebagai daftar daftar string multi-baris.
Cobalah secara online (cukup tercetak; jangan ragu untuk menghapus footer untuk melihat hasil sebenarnya).
Penjelasan:
v # Loop `y` over each string of the (implicit) input-list:
yD # Push line `y` twice
']¡ '# Split the copy on "]"
н # Only leave the first part
¦ # Remove the leading character (the potential "[")
D # Duplicate it
žj # Push builtin string "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
à # Only keep those characters in the string we duplicated
Êi # If it is NOT equal to the string:
‚ # Pair it with the previous line
» # And join that pair with a newline delimiter
] # Close both the if-statement and loop
) # Wrap all values on the stack into a list
.¡ # Group all strings by:
# # Split the string on spaces
н # And only leave the first part (the tag)
# (after which the result is output implicitly)
AWK-F] , 122 123113 byte
Menambahkan satu byte untuk memperbaiki bug yang ditunjukkan oleh water_ghosts .
Menyimpan 10 byte berkat Giuseppe !!!
/^\[\w+\]/{a[l=$1][i++]=$2;next}{a[l][i++]=$0}END{for(k in a){print"\n",substr(k,2);for(j in a[k])print a[k][j]}}
SimpleTemplate, 142 byte
Nah, ini tidak terlalu sulit.
Jawaban ini adalah versi yang sedikit dimodifikasi dari: Garis log de-interleave
{@callexplode intoL EOL,argv.0}{@eachL}{@if_ matches"@^(\[\w+\]) ?(.+)$@"M}{@setX"#{M.1} "}{@set_ M.2}{@/}{@setS.[X]S.[X],X,_,EOL}{@/}{@echoS}
Ungolfed:
Karena ini cukup tidak terbaca, di bawah ini adalah versi yang dapat dibaca:
{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
{@if line matches "@^(\[\w+\]) ?(.+)$@" match}
{@set last "#{match.1} "}
{@set line match.2}
{@/}
{@set storage.[last] storage.[last], last, line, EOL}
{@/}
{@echo storage}
Perubahan:
Beberapa perubahan harus dilakukan agar berfungsi dengan baik, dengan persyaratan baru. Di bawah ini adalah salinan dari jawaban yang ditautkan:
{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
{@if line matches "@^(\[.*\])@" match}
{@set storage.[match.1] storage.[match.1], line, EOL}
{@/}
{@/}
{@echo storage}
Di bawah ini adalah daftar lengkap perubahannya:
- Ekspresi reguler diubah untuk mencocokkan konten lainnya, tanpa spasi, jika ada. (Mengutip: "Akan selalu ada setidaknya satu karakter non-spasi setelah tag.")
- Menyimpan "aplikasi" dengan spasi ekstra, untuk digunakan nanti dan untuk menormalkan baris (yang mungkin memiliki atau tidak memiliki spasi tepat setelah "tag").
- Menyimpan konten yang tersisa, tanpa spasi pertama, ke dalam variabel
line(_untuk versi golf) - Menambahkan "tag" sebelum
linevariabel, yang dulunya merupakan bagian darilinevariabel.
Seperti yang Anda lihat, perubahannya tidak terlalu signifikan. Pindahkan kode, tambahkan spasi ekstra, tambahkan variabel ke output.
Anda dapat mencobanya di: http://sandbox.onlinephpfunctions.com/code/eb5380ba1826530087fd92fa71d709c0b2d6de39
Scala, 127 byte
l=>((("",List[(String,String)]())/:l){case((p,m),s"[$t] $b")=>(t,(t,b)::m)case((p,m),b)=>(p,(p,b)::m)})._2.groupMap(_._1)(_._2)
Cobalah di Scastie (tidak berfungsi di TIO)
Wow, ini panjang.