Saya ingin menghapus semua karakter khusus dari string. Karakter yang diizinkan adalah AZ (huruf besar atau kecil), angka (0-9), garis bawah (_), atau tanda titik (.).
Saya memiliki yang berikut, ini berfungsi tetapi saya curiga (saya tahu!) Itu tidak terlalu efisien:
public static string RemoveSpecialCharacters(string str)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
if ((str[i] >= '0' && str[i] <= '9')
|| (str[i] >= 'A' && str[i] <= 'z'
|| (str[i] == '.' || str[i] == '_')))
{
sb.Append(str[i]);
}
}
return sb.ToString();
}
Apa cara paling efisien untuk melakukan ini? Seperti apa ekspresi reguler, dan bagaimana perbandingannya dengan manipulasi string normal?
String yang akan dibersihkan akan lebih pendek, biasanya antara 10 dan 30 karakter.
Jawaban:
Menurut Anda mengapa metode Anda tidak efisien? Ini sebenarnya salah satu cara paling efisien yang dapat Anda lakukan.
Tentu saja Anda harus membaca karakter menjadi variabel lokal atau menggunakan enumerator untuk mengurangi jumlah akses array:
Satu hal yang membuat metode seperti ini efisien adalah bahwa ia berskala dengan baik. Waktu eksekusi akan relatif terhadap panjang string. Tidak ada kejutan buruk jika Anda menggunakannya pada string besar.
Sunting:
Saya membuat tes kinerja cepat, menjalankan setiap fungsi sejuta kali dengan string 24 karakter. Inilah hasilnya:
Fungsi asli: 54,5 ms.
Perubahan yang saya sarankan: 47.1 ms.
Milik saya dengan pengaturan kapasitas StringBuilder: 43,3 ms.
Ekspresi reguler: 294,4 ms.
Sunting 2: Saya menambahkan perbedaan antara AZ dan az dalam kode di atas. (Saya mengulangi tes kinerja, dan tidak ada perbedaan nyata.)
Sunting 3:
Saya menguji solusi pencarian + char [], dan itu berjalan sekitar 13 ms.
Harga yang harus dibayar, tentu saja, inisialisasi tabel pencarian besar dan menyimpannya dalam memori. Yah, itu tidak banyak data, tapi itu banyak untuk fungsi sepele seperti itu ...
sumber
char[]
buffer daripadaStringBuilder
, memiliki sedikit keunggulan pada yang ini menurut pengujian saya. (Meskipun kurang dibaca Tambang, sehingga manfaat kinerja kecil mungkin tidak layak.)char[]
buffer berkinerja (sedikit) lebih baik daripadaStringBuilder
, bahkan ketika meningkatkan ke string yang panjangnya puluhan ribu karakter.Nah, kecuali Anda benar-benar perlu memeras kinerja dari fungsi Anda, ikuti saja apa yang paling mudah untuk dipelihara dan dipahami. Ekspresi reguler akan terlihat seperti ini:
Untuk kinerja tambahan, Anda dapat melakukan pra-kompilasi atau hanya mengatakannya untuk dikompilasi pada panggilan pertama (panggilan berikutnya akan lebih cepat.)
sumber
Saya sarankan membuat tabel pencarian sederhana, yang dapat Anda inisialisasi dalam konstruktor statis untuk mengatur kombinasi karakter apa pun menjadi valid. Ini memungkinkan Anda melakukan pemeriksaan cepat dan tunggal.
sunting
Juga, untuk kecepatan, Anda ingin menginisialisasi kapasitas StringBuilder Anda hingga panjang string input Anda. Ini akan menghindari realokasi. Kedua metode ini bersama-sama akan memberi Anda kecepatan dan fleksibilitas.
suntingan lain
Saya pikir kompiler mungkin mengoptimalkannya, tetapi sebagai masalah gaya dan efisiensi, saya sarankan foreach bukan untuk.
sumber
for
danforeach
menghasilkan kode serupa. Saya tidak tahu tentang string. Saya ragu bahwa JIT tahu tentang sifat String seperti array.sumber
foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Ekspresi reguler akan terlihat seperti:
Tetapi jika kinerja sangat penting, saya sarankan Anda untuk melakukan beberapa tolok ukur sebelum memilih "jalur regex" ...
sumber
Jika Anda menggunakan daftar karakter yang dinamis, LINQ mungkin menawarkan solusi yang jauh lebih cepat dan anggun:
Saya membandingkan pendekatan ini dengan dua pendekatan "cepat" sebelumnya (kompilasi rilis):
Perhatikan bahwa algoritma sedikit dimodifikasi - karakter dilewatkan sebagai array daripada hard-coded, yang bisa berdampak sedikit (yaitu / solusi lain akan memiliki loop foor dalam untuk memeriksa array karakter).
Jika saya beralih ke solusi kode keras menggunakan LINQ mana klausa, hasilnya adalah:
Mungkin layak untuk melihat LINQ atau pendekatan yang dimodifikasi jika Anda berencana untuk menulis solusi yang lebih umum, daripada mengkode daftar karakter. LINQ jelas memberi Anda kode ringkas dan sangat mudah dibaca - bahkan lebih dari Regex.
sumber
Saya tidak yakin algoritme Anda sama sekali tidak efisien. Ini O (n) dan hanya melihat setiap karakter satu kali. Anda tidak akan mendapatkan yang lebih baik dari itu kecuali Anda secara ajaib mengetahui nilai-nilai sebelum memeriksanya.
Namun saya akan menginisialisasi kapasitas Anda
StringBuilder
ke ukuran awal string. Saya menduga masalah kinerja Anda yang dirasakan berasal dari realokasi memori.Catatan: Memeriksa
A
-z
tidak aman. Anda termasuk[
,\
,]
,^
,_
, dan `...Catatan sisi 2: Untuk itu sedikit efisiensi, menempatkan perbandingan agar memperkecil jumlah perbandingan. (Paling buruk, Anda berbicara 8 perbandingan, jadi jangan berpikir terlalu keras.) Ini berubah dengan input yang Anda harapkan, tetapi salah satu contohnya adalah:
Catatan sisi 3: Jika karena alasan apa pun Anda BENAR-BENAR membutuhkan ini cepat, pernyataan pergantian mungkin lebih cepat. Kompiler harus membuat tabel lompatan untuk Anda, sehingga hanya menghasilkan satu perbandingan:
sumber
sumber
Anda dapat menggunakan ekspresi reguler sebagai berikut:
sumber
Sepertinya baik untuk saya. Satu-satunya perbaikan yang akan saya lakukan adalah menginisialisasi
StringBuilder
dengan panjang string.sumber
Saya setuju dengan contoh kode ini. Satu-satunya yang berbeda saya membuatnya menjadi Metode Ekstensi tipe string. Sehingga Anda dapat menggunakannya dalam baris atau kode yang sangat sederhana:
Terima kasih kepada Guffa untuk eksperimen Anda.
sumber
Saya akan menggunakan Ganti String dengan Ekspresi Reguler mencari "karakter khusus", menggantikan semua karakter yang ditemukan dengan string kosong.
sumber
Saya harus melakukan sesuatu yang serupa untuk pekerjaan, tetapi dalam kasus saya, saya harus memfilter semua yang bukan huruf, angka, atau spasi putih (tetapi Anda dapat dengan mudah memodifikasinya sesuai kebutuhan Anda). Penyaringan dilakukan sisi klien dalam JavaScript, tetapi untuk alasan keamanan saya juga melakukan penyaringan sisi server. Karena saya bisa berharap sebagian besar string menjadi bersih, saya ingin menghindari menyalin string kecuali saya benar-benar perlu. Ini memungkinkan saya untuk implementasi di bawah ini, yang seharusnya berkinerja lebih baik untuk string bersih dan kotor.
sumber
Untuk S&G, cara Linq-ified:
Saya tidak berpikir ini akan menjadi cara yang paling efisien.
sumber
sumber
Menggunakan:
Dan Anda akan mendapatkan string yang bersih
s
.erase()
akan menghapus semua karakter khusus dan sangat dapat disesuaikan denganmy_predicate()
fungsinya.sumber
HashSet adalah O (1)
Tidak yakin apakah itu lebih cepat dari perbandingan yang ada
Saya menguji dan ini tidak lebih cepat dari jawaban yang diterima.
Saya akan membiarkannya seolah-olah Anda membutuhkan serangkaian karakter yang dapat dikonfigurasi ini akan menjadi solusi yang baik.
sumber
Saya ingin tahu apakah pengganti berbasis Regex (mungkin dikompilasi) lebih cepat.
Harus mengujinyaSeseorang telah menemukan ini menjadi ~ 5 kali lebih lambat.Selain itu, Anda harus menginisialisasi StringBuilder dengan panjang yang diharapkan, sehingga string menengah tidak harus disalin ketika sedang tumbuh.
Angka yang baik adalah panjang string asli, atau sesuatu yang sedikit lebih rendah (tergantung pada sifat input fungsi).
Akhirnya, Anda bisa menggunakan tabel pencarian (dalam kisaran 0..127) untuk mengetahui apakah karakter akan diterima.
sumber
Kode berikut memiliki output berikut (kesimpulannya adalah kita juga dapat menyimpan beberapa sumber daya memori yang mengalokasikan ukuran array yang lebih kecil):
Anda juga dapat menambahkan baris kode berikut untuk mendukung lokal Rusia (ukuran array akan 1104):
sumber
Saya tidak yakin itu adalah cara yang paling efisien, tetapi ini bekerja untuk saya
sumber
Ada banyak solusi yang diusulkan di sini, beberapa lebih efisien daripada yang lain, tetapi mungkin tidak terlalu mudah dibaca. Inilah salah satu yang mungkin bukan yang paling efisien, tetapi tentu dapat digunakan untuk sebagian besar situasi, dan cukup ringkas dan mudah dibaca, meningkatkan Linq:
sumber
sumber
replaceAll
ini bukan fungsi C # String tetapi Java atau JavaScriptsumber
Jika Anda khawatir tentang kecepatan, gunakan pointer untuk mengedit string yang ada. Anda bisa menyematkan string dan mendapatkan pointer ke sana, lalu jalankan for for loop di atas setiap karakter, timpa setiap karakter yang tidak valid dengan karakter pengganti. Ini akan sangat efisien dan tidak perlu mengalokasikan memori string baru. Anda juga perlu mengkompilasi modul Anda dengan opsi yang tidak aman, dan menambahkan pengubah "tidak aman" ke header metode Anda untuk menggunakan pointer.
sumber