Cara paling efisien untuk menghapus karakter khusus dari string

266

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.

ObiWanKenobi
sumber
5
Saya tidak akan memasukkan ini dalam jawaban karena tidak akan lebih efisien, tetapi ada sejumlah metode char statis seperti char.IsLetterOrDigit () yang dapat Anda gunakan dalam pernyataan if Anda untuk membuatnya lebih mudah dibaca setidaknya.
Martin Harris
5
Saya tidak yakin memeriksa A hingga z aman, karena membawa 6 karakter yang tidak sesuai abjad, hanya satu yang diinginkan (underbar).
Steven Sudit
4
Fokus untuk membuat kode Anda lebih mudah dibaca. kecuali jika Anda melakukan ini dalam satu lingkaran seperti 500 kali per detik, efisiensi bukan masalah besar. Gunakan regexp dan akan jauh lebih mudah dibaca.L
Byron Whitlock
4
Byron, Anda mungkin benar perlu menekankan keterbacaan. Namun, saya ragu regexp bisa dibaca. :-)
Steven Sudit
2
Ekspresi reguler yang bisa dibaca atau tidak mirip dengan bahasa Jerman yang bisa dibaca atau tidak; itu tergantung pada apakah Anda tahu atau tidak (meskipun dalam kedua kasus Anda akan setiap sekarang dan kemudian menemukan aturan tata bahasa yang tidak masuk akal;)
Blixt

Jawaban:

325

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:

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

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 ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}
Guffa
sumber
4
Saya setuju. Satu-satunya perubahan lain yang akan saya lakukan adalah menambahkan argumen kapasitas awal ke konstruktor StringBuilder, "= new StringBuilder (str.Length)".
David
2
Jawaban saya, menggunakan char[]buffer daripada StringBuilder, memiliki sedikit keunggulan pada yang ini menurut pengujian saya. (Meskipun kurang dibaca Tambang, sehingga manfaat kinerja kecil mungkin tidak layak.)
LukeH
1
@ Seven: Mungkin itu masalahnya, tetapi tolok ukur berbicara sendiri! Dalam pengujian saya, menggunakan char[]buffer berkinerja (sedikit) lebih baik daripada StringBuilder, bahkan ketika meningkatkan ke string yang panjangnya puluhan ribu karakter.
LukeH
10
@ downvoter: Mengapa downvote? Jika Anda tidak menjelaskan apa yang Anda pikir salah, itu tidak dapat meningkatkan jawabannya.
Guffa
2
@ SILENT: Tidak, tidak, tetapi Anda hanya harus melakukannya sekali. Jika Anda mengalokasikan array yang besar setiap kali Anda memanggil metode (dan jika Anda sering memanggil metode) maka metode menjadi yang paling lambat sejauh ini, dan menyebabkan banyak pekerjaan untuk pengumpul sampah.
Guffa
195

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.)

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
Blixt
sumber
1
Saya kira ini mungkin permintaan yang cukup kompleks sehingga akan lebih cepat daripada pendekatan OP, terutama jika pra-kompilasi. Saya tidak punya bukti untuk mendukung itu. Itu harus diuji. Kecuali jika secara drastis lebih lambat, saya akan memilih pendekatan ini, karena cara ini lebih mudah dibaca dan dipelihara. +1
rmeador
6
Ini adalah regex yang sangat sederhana (tidak ada backtracking atau hal-hal rumit di sana) sehingga harus sangat cepat.
9
@ rmeador: tanpa dikompilasi itu sekitar 5x lebih lambat, dikompilasi lebih lambat 3x dari metodenya. Masih 10x lebih sederhana meskipun :-D
user7116
6
Ekspresi reguler bukanlah palu ajaib dan tidak pernah lebih cepat dari kode yang dioptimalkan dengan tangan.
Christian Klauser
2
Bagi mereka yang ingat kutipan terkenal Knuth tentang optimisasi, ini adalah tempat untuk memulai. Kemudian, jika Anda menemukan bahwa Anda membutuhkan seperseribu ekstra dari kinerja milidetik, lanjutkan dengan salah satu teknik lainnya.
John
15

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.

Steven Sudit
sumber
Untuk array, fordan foreachmenghasilkan kode serupa. Saya tidak tahu tentang string. Saya ragu bahwa JIT tahu tentang sifat String seperti array.
Christian Klauser
1
Saya yakin JIT tahu lebih banyak tentang sifat string array-like daripada [lelucon Anda dihapus]. Anders dkk melakukan banyak pekerjaan mengoptimalkan segala sesuatu tentang string dalam .net
Saya sudah melakukan ini menggunakan HashSet <char> dan ini sekitar 2x lebih lambat dari metodenya. Menggunakan bool [] hampir tidak lebih cepat (0,0469 ms / iter v. 0,0559 ms / iter) daripada versi yang dimilikinya dalam OP ... dengan masalah kurang dapat dibaca.
user7116
1
Saya tidak bisa melihat perbedaan kinerja antara menggunakan array bool dan array int. Saya akan menggunakan array bool, karena membawa turun tabel pencarian dari 256 kb ke 64 kb, tapi masih banyak data untuk fungsi sepele seperti itu ... Dan itu hanya sekitar 30% lebih cepat.
Guffa
1
@Guffa 2) Mengingat kami hanya menyimpan alfanumerik dan beberapa karakter Latin Dasar, kami hanya membutuhkan tabel untuk byte rendah, jadi ukurannya tidak terlalu menjadi masalah. Jika kita ingin menjadi tujuan umum, maka teknik Unicode standar adalah tipuan ganda. Dengan kata lain, tabel 256 tabel referensi, banyak yang menunjuk ke tabel kosong yang sama.
Steven Sudit
12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}
LukeH
sumber
1
+1, teruji dan sekitar 40% lebih cepat dari StringBuilder. 0,0294ms / string v. 0,0399ms / string
user7116
Hanya untuk memastikan, maksud Anda StringBuilder dengan atau tanpa pra-alokasi?
Steven Sudit
Dengan pra-alokasi, masih lebih lambat 40% dari alokasi char [] dan string baru.
user7116
2
Saya suka ini. Saya men-tweak metode iniforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic
11

Ekspresi reguler akan terlihat seperti:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

Tetapi jika kinerja sangat penting, saya sarankan Anda untuk melakukan beberapa tolok ukur sebelum memilih "jalur regex" ...

CMS
sumber
11

Jika Anda menggunakan daftar karakter yang dinamis, LINQ mungkin menawarkan solusi yang jauh lebih cepat dan anggun:

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

Saya membandingkan pendekatan ini dengan dua pendekatan "cepat" sebelumnya (kompilasi rilis):

  • Solusi array char oleh LukeH - 427 ms
  • Solusi StringBuilder - 429 ms
  • LINQ (jawaban ini) - 98 ms

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:

  • Solusi array char - 7ms
  • Solusi StringBuilder - 22 ms
  • LINQ - 60 ms

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.

ShadowChaser
sumber
3
Pendekatan ini terlihat bagus, tetapi tidak berhasil - Kecuali () adalah operasi yang ditetapkan, sehingga Anda hanya akan berakhir dengan penampilan pertama dari setiap karakter unik dalam string.
McKenzieG1
5

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 StringBuilderke ukuran awal string. Saya menduga masalah kinerja Anda yang dirasakan berasal dari realokasi memori.

Catatan: Memeriksa A- ztidak 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:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

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:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}
lc.
sumber
1
Saya setuju bahwa Anda tidak dapat mengalahkan O (n) untuk yang satu ini. Namun, ada biaya per perbandingan yang dapat diturunkan. Tabel lookup memiliki biaya tetap yang rendah, sementara serangkaian perbandingan akan meningkatkan biaya saat Anda menambahkan lebih banyak pengecualian.
Steven Sudit
Tentang catatan sisi 3, apakah Anda benar-benar berpikir tabel lompatan akan lebih cepat daripada pencarian tabel?
Steven Sudit
Saya menjalankan tes kinerja cepat pada solusi sakelar, dan kinerjanya sama dengan perbandingan.
Guffa
@ Seven Sudit - Saya berani usaha mereka sebenarnya hampir sama. Mau menjalankan tes?
lc.
7
O (n) notasi terkadang membuatku kesal. Orang-orang akan membuat asumsi bodoh berdasarkan fakta bahwa algoritma tersebut sudah O (n). Jika kami mengubah rutin ini untuk menggantikan panggilan str [i] dengan fungsi yang mengambil nilai perbandingan dengan membangun koneksi SSL satu kali dengan server di sisi yang berlawanan dari dunia ... Anda pasti akan melihat kinerja besar-besaran Perbedaan dan algoritma adalah MASIH O (n). Biaya O (1) untuk setiap algoritma signifikan dan TIDAK setara!
darron
4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}
Chamika Sandamal
sumber
4

Anda dapat menggunakan ekspresi reguler sebagai berikut:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));
Giovanny Farto M.
sumber
3

Sepertinya baik untuk saya. Satu-satunya perbaikan yang akan saya lakukan adalah menginisialisasi StringBuilderdengan panjang string.

StringBuilder sb = new StringBuilder(str.Length);
bruno conde
sumber
3

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:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

Terima kasih kepada Guffa untuk eksperimen Anda.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}
Tola Ch.
sumber
2

Saya akan menggunakan Ganti String dengan Ekspresi Reguler mencari "karakter khusus", menggantikan semua karakter yang ditemukan dengan string kosong.

Stephen Wrighton
sumber
+1 pastinya lebih sedikit kode dan bisa dibilang lebih bisa dibaca mengabaikan write-once Regex.
kenny
1
@kenny - Saya setuju. Pertanyaan aslinya bahkan menyatakan bahwa senarnya pendek - 10-30 karakter. Tapi ternyata banyak orang masih berpikir kami menjual waktu CPU pada detik ...
Tom Bushell
Reguler expressin bekerja sangat malas. Jadi seharusnya tidak selalu digunakan.
RockOnGom
2

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.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}
Daniel Blankensteiner
sumber
1

Untuk S&G, cara Linq-ified:

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

Saya tidak berpikir ini akan menjadi cara yang paling efisien.


sumber
2
Bukan, karena ini adalah pencarian linear.
Steven Sudit
1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}
Shiko
sumber
1

Menggunakan:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

Dan Anda akan mendapatkan string yang bersih s.

erase()akan menghapus semua karakter khusus dan sangat dapat disesuaikan dengan my_predicate()fungsinya.

Bhavya Agarwal
sumber
1

HashSet adalah O (1)
Tidak yakin apakah itu lebih cepat dari perbandingan yang ada

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

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.

paparazzo
sumber
Menurut Anda mengapa perbandingannya bukan O (1)?
Guffa
@ Guffa Saya tidak yakin tidak dan saya menghapus komentar saya. Dan +1. Saya seharusnya melakukan lebih banyak pengujian sebelum membuat komentar.
paparazzo
1

Saya ingin tahu apakah pengganti berbasis Regex (mungkin dikompilasi) lebih cepat. Harus mengujinya Seseorang 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.

Christian Klauser
sumber
Ekspresi reguler telah diuji, dan sekitar lima kali lebih lambat. Dengan tabel pencarian di kisaran 0..127 Anda masih harus memeriksa kode karakter sebelum menggunakan tabel pencarian, karena karakter adalah nilai 16 bit, bukan 7 nilai bit.
Guffa
@Guffa Err ... ya? ;)
Christian Klauser
1

Kode berikut memiliki output berikut (kesimpulannya adalah kita juga dapat menyimpan beberapa sumber daya memori yang mengalokasikan ukuran array yang lebih kecil):

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

Anda juga dapat menambahkan baris kode berikut untuk mendukung lokal Rusia (ukuran array akan 1104):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}
Pavel Shkleinik
sumber
1

Saya tidak yakin itu adalah cara yang paling efisien, tetapi ini bekerja untuk saya

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function
RonaldPaguay
sumber
Jawabannya tidak bekerja, tapi pertanyaannya adalah untuk C #. (PS: Saya tahu ini praktis lima tahun yang lalu, tapi tetap saja ..) Saya menggunakan Telerik VB ke C # Converter, (Dan sebaliknya) dan kodenya berfungsi dengan baik - meskipun tidak yakin dengan orang lain. (Hal lain, converter.telerik.com )
Momoro
1

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:

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());
Steve Faiwiszewski
sumber
-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}
Jawaid
sumber
1
Saya khawatir replaceAllini bukan fungsi C # String tetapi Java atau JavaScript
Csaba Toth
-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}
Hasan_H
sumber
Jawabannya salah. Jika Anda akan menggunakan regex, itu harus inklusif, bukan yang eksklusif, karena Anda kehilangan beberapa karakter sekarang. Sebenarnya, sudah ada jawaban dengan regex. Dan untuk menjadi full - regex adalah SLOWER maka langsung bandingkan fungsi karakter.
TPAKTOPA
-3

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.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}
Triynko
sumber
14
Tidaaaaaaaak! Mengubah string di .NET adalah BAAAAAAAAAAAAD! Segala sesuatu dalam kerangka kerja bergantung pada aturan bahwa string tidak dapat diubah, dan jika Anda memutuskan bahwa Anda bisa mendapatkan efek samping yang sangat mengejutkan ...
Guffa