Saya melakukan sesuatu di mana saya sadar saya ingin menghitung berapa /
dapat saya temukan dalam sebuah string, dan kemudian saya tersadar, bahwa ada beberapa cara untuk melakukannya, tetapi tidak dapat memutuskan apa yang terbaik (atau yang paling mudah) adalah .
Saat ini saya akan dengan sesuatu seperti:
string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;
Tapi saya tidak suka sama sekali, tidak ada yang mengambil?
Saya benar-benar tidak ingin menggali RegEx
untuk ini, bukan?
Saya tahu string saya akan memiliki istilah yang saya cari, sehingga Anda dapat menganggap bahwa ...
Tentu saja untuk string di mana panjang> 1 ,
string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N",""))
.Jawaban:
Jika Anda menggunakan .NET 3.5 Anda bisa melakukan ini dalam satu-liner dengan LINQ:
Jika Anda tidak ingin menggunakan LINQ Anda dapat melakukannya dengan:
Anda mungkin terkejut mengetahui bahwa teknik asli Anda tampaknya sekitar 30% lebih cepat daripada salah satu dari ini! Saya baru saja melakukan patokan cepat dengan "/ Once / upon / a / time /" dan hasilnya adalah sebagai berikut:
(Waktu untuk 50.000.000 iterasi sehingga Anda tidak akan melihat banyak perbedaan di dunia nyata.)
sumber
f == '\'
adalah tentang karakter dalam string, bukan string dalam stringHarus lebih cepat daripada
source.Replace()
dengan sendirinya.sumber
sumber
RegexOptions.IgnoreCase
.Regex.Escape(...)
begitunew System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
Jika Anda ingin dapat mencari seluruh string, dan bukan hanya karakter:
Baca sebagai "untuk setiap karakter dalam string, ambil sisa string mulai dari karakter itu sebagai substring; hitung jika dimulai dengan string target."
sumber
Saya telah melakukan beberapa penelitian dan menemukan bahwa solusi Richard Watson paling cepat dalam banyak kasus. Itulah tabel dengan hasil setiap solusi di posting (kecuali yang menggunakan Regex karena ia melempar pengecualian sambil mengurai string seperti "test {test")
Anda dapat melihat bahwa jika menemukan jumlah kemunculan substring pendek (1-5 karakter) dalam string pendek (10-50 karakter), algoritma asli lebih disukai.
Selain itu, untuk substring multicharacter Anda harus menggunakan kode berikut (berdasarkan solusi Richard Watson )
sumber
Regex.Escape(needle)
source="aaa" substring="aa"
saya diharapkan untuk kembali 2, bukan 1. Untuk "memperbaiki" ini, ubahn += substring.Length
ken++
overlapped
bendera untuk memenuhioverlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
LINQ berfungsi pada semua koleksi, dan karena string hanyalah kumpulan karakter, bagaimana dengan one-liner kecil yang menyenangkan ini:
Pastikan Anda memiliki
using System.Linq;
di bagian atas file kode Anda, seperti.Count
metode ekstensi dari namespace itu.sumber
int
semua huruf berada di tombol home, sementaravar
tidak. uh .. tunggu, saya menggunakan DvorakDi komputer saya ini sekitar 2 detik lebih cepat daripada solusi untuk setiap karakter untuk 50 juta iterasi.
Revisi 2013:
Ubah string menjadi char [] dan ulangi itu. Memotong satu atau dua detik lebih lanjut dari total waktu untuk iterasi 50m!
Ini masih lebih cepat:
Untuk ukuran yang baik, iterasi dari akhir array ke 0 tampaknya menjadi yang tercepat, sekitar 5%.
Saya bertanya-tanya mengapa ini bisa dan sedang Googling di sekitar (saya ingat sesuatu tentang membalikkan iterasi menjadi lebih cepat), dan menemukan pertanyaan SO yang mengganggu menggunakan string untuk teknik char [] sudah. Saya pikir trik pembalikan itu baru dalam konteks ini.
Apa cara tercepat untuk beralih melalui karakter individual dalam sebuah string dalam C #?
sumber
source.IndexOf('/', n + 1)
dan kehilangann++
tanda kurung sementara :) Juga, letakkan variabelstring word = "/"
sebagai ganti karakter.sumber
Keduanya hanya berfungsi untuk istilah pencarian satu karakter ...
mungkin menjadi lebih baik untuk jarum yang lebih panjang ...
Tetapi harus ada cara yang lebih elegan. :)
sumber
Edit:
sumber
source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1
untuk pemisah multi-karakter.Di C #, penghitung String SubString yang bagus adalah orang yang sulitnya tak terduga ini:
sumber
sumber
stringToMatch
kebutuhan melarikan diri, bukaninput
.Karena solusi asli, adalah yang tercepat untuk karakter, saya kira itu juga untuk string. Jadi, inilah kontribusi saya.
Untuk konteksnya: Saya mencari kata-kata seperti 'gagal' dan 'berhasil' dalam file log.
Gr, Ben
sumber
sumber
Bagi siapa pun yang menginginkan metode ekstensi String yang siap digunakan,
di sini adalah apa yang saya gunakan yang didasarkan pada yang terbaik dari jawaban yang diposting:
sumber
sumber
Saya pikir cara termudah untuk melakukan ini adalah dengan menggunakan Ekspresi Reguler. Dengan cara ini Anda bisa mendapatkan jumlah pemisahan yang sama seperti yang Anda bisa menggunakan myVar.Split ('x') tetapi dalam pengaturan beberapa karakter.
sumber
Ini akan dihitung setiap kali program menemukan "/ s" tepat (case-sensitive) dan jumlah kemunculan ini akan disimpan dalam variabel "kemunculan"
sumber
Saya merasa bahwa kami kekurangan jenis penghitungan sub string tertentu, seperti perbandingan byte-by-byte yang tidak aman. Saya mengumpulkan metode poster asli dan metode apa pun yang dapat saya pikirkan.
Ini adalah ekstensi string yang saya buat.
Diikuti oleh kode tes ...
Hasil: CSX sesuai dengan CountSubstrX dan CCX sesuai dengan CountCharX. "chr" mencari string untuk '_', "dan" mencari string untuk "dan", dan "mlw" mencari string untuk "muchlongerword"
Dan akhirnya, saya punya file dengan 3,6 juta karakter. Itu "derp adfderdserp dfaerpderp deasderp" diulang 100.000 kali. Saya mencari "derp" di dalam file dengan metode di atas 100 kali hasil ini.
Jadi metode ke-4 saya pasti pemenangnya, tetapi, secara realistis, jika file 3,6 juta karakter 100 kali hanya mengambil 1586 ms sebagai kasus terburuk, maka semua ini cukup dapat diabaikan.
Ngomong-ngomong, saya juga memindai 'd' char dalam file 3,6 juta karakter dengan 100 kali metode CountSubstr dan CountChar. Hasil ...
Metode poster asli sangat buruk untuk jarum karakter tunggal di tumpukan jerami besar menurut ini.
Catatan: Semua nilai diperbarui ke Keluaran versi keluaran. Saya tidak sengaja lupa untuk membangun mode Rilis setelah pertama kali saya memposting ini. Beberapa pernyataan saya telah diubah.
sumber
Fungsi generik untuk kemunculan string:
sumber
Variasi pada jawaban Richard Watson, sedikit lebih cepat dengan peningkatan efisiensi semakin banyak kali karakter muncul dalam string, dan semakin sedikit kode!
Meskipun saya harus mengatakan, tanpa menguji setiap skenario secara ekstensif, saya memang melihat peningkatan kecepatan yang sangat signifikan dengan menggunakan:
sumber
Diperlukan untuk melakukan sesuatu yang mirip dengan menguji pernyataan bersyarat dari string.
Mengganti apa yang saya cari dengan satu karakter dan menghitung instance dari karakter tunggal.
Jelas karakter tunggal yang Anda gunakan harus diperiksa untuk tidak ada dalam string sebelum ini terjadi untuk menghindari penghitungan yang salah.
sumber
String dalam string:
Temukan "dll" di ".. JD JD JD JD dll. Dan lain-lain. JDJDJDJDJDJDJDJD dan sebagainya."
Periksa kinerja sebelum membuang yang ini sebagai tidak sehat / canggung ...
sumber
Pengambilan awal saya memberi saya sesuatu seperti:
Jarum dalam pendekatan tumpukan jerami menggunakan penggantian dan pembagian menghasilkan 21+ detik sedangkan ini membutuhkan waktu sekitar 15,2.
Edit setelah menambahkan sedikit yang akan ditambahkan
substring.Length - 1
ke charIndex (seperti seharusnya), itu di 11,6 detik.Sunting 2: Saya menggunakan string yang memiliki 26 string dua karakter, berikut adalah waktu yang diperbarui ke teks sampel yang sama:
Jarum di tumpukan jerami (versi OP): 7,8 Detik
Mekanisme yang disarankan: 4,6 detik.
Sunting 3: Menambahkan huruf sudut karakter tunggal, ia pergi ke 1,2 detik.
Sunting 4: Untuk konteks: 50 juta iterasi digunakan.
sumber
Saya pikir saya akan membuang metode ekstensi saya ke ring (lihat komentar untuk info lebih lanjut). Saya belum melakukan penandaan bangku formal, tapi saya pikir itu harus sangat cepat untuk sebagian besar skenario.
EDIT: OK - jadi pertanyaan SO ini membuat saya bertanya-tanya bagaimana kinerja implementasi kami saat ini akan menumpuk terhadap beberapa solusi yang disajikan di sini. Saya memutuskan untuk melakukan penilaian bangku kecil dan menemukan bahwa solusi kami sangat sejalan dengan kinerja solusi yang disediakan oleh Richard Watson hingga Anda melakukan pencarian agresif dengan string besar (100 Kb +), substring besar (32 Kb + ) dan banyak pengulangan tertanam (10K +). Pada saat itu solusi kami sekitar 2X hingga 4X lebih lambat. Mengingat ini dan fakta bahwa kami benar-benar menyukai solusi yang disajikan oleh Richard Watson, kami telah refactored solusi kami sesuai. Saya hanya ingin membuat ini tersedia untuk siapa saja yang mungkin mendapat manfaat dari itu.
Solusi asli kami:
Dan inilah solusi kami yang telah direvisi:
sumber
sumber
Itu hanya memeriksa setiap karakter dalam string, jika karakter adalah karakter yang Anda cari, tambahkan satu untuk dihitung.
sumber
Jika Anda melihat halaman web ini , 15 cara berbeda untuk melakukan benchmark ini, termasuk menggunakan loop paralel.
Cara tercepat tampaknya menggunakan salah satu ulir tunggal untuk-loop (jika Anda memiliki .Net versi <4.0) atau parallel.for loop (jika menggunakan .Net> 4.0 dengan ribuan pemeriksaan).
Dengan asumsi "ss" adalah String Pencarian Anda, "ch" adalah array karakter Anda (jika Anda memiliki lebih dari satu karakter yang Anda cari), inilah inti dasar dari kode yang memiliki waktu menjalankan tercepat single threaded:
Kode sumber patokan disediakan juga sehingga Anda dapat menjalankan tes Anda sendiri.
sumber
Ini untuk menghitung terjadinya karakter. Untuk contoh ini output akan menjadi "a4b4j3"
sumber
Untuk kasus pembatas string (bukan untuk kasus char, seperti yang dikatakan subjek):
string source = "@@@ once @@@ pada @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries). Panjang - 1;
Pembatas alami nilai asli poster itu ("/ once / upon / a / time /") adalah char '/' dan responsnya menjelaskan opsi source.plit (char []) ...
sumber
menggunakan System.Linq;
int CountOf => "A :: BC :: D" .Split ("::"). Panjang - 1;
sumber