Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
.NET memberikan dukungan ekstensif untuk mengembangkan aplikasi yang dilokalkan dan di globalisasi, dan memudahkan penerapan konvensi budaya saat ini atau budaya tertentu saat melakukan operasi umum seperti mengurutkan dan menampilkan string. Tetapi mengurutkan atau membandingkan string tidak selalu merupakan operasi yang sensitif terhadap budaya. Misalnya, string yang digunakan secara internal oleh aplikasi biasanya harus ditangani secara identik di semua budaya. Ketika data string independen secara budaya, seperti tag XML, tag HTML, nama pengguna, jalur file, dan nama objek sistem, ditafsirkan seolah-olah mereka peka terhadap budaya, kode aplikasi dapat tunduk pada bug halus, performa yang buruk, dan, dalam beberapa kasus, masalah keamanan.
Artikel ini memeriksa metode pengurutan string, perbandingan, dan casing di .NET, menyajikan rekomendasi untuk memilih metode penanganan string yang sesuai, dan memberikan informasi tambahan tentang metode penanganan string.
Rekomendasi untuk penggunaan string
Saat Anda mengembangkan dengan .NET, ikuti rekomendasi ini saat Anda membandingkan string.
Petunjuk / Saran
Berbagai metode terkait string melakukan perbandingan. Contohnya termasuk String.Equals, , String.CompareString.IndexOf, dan String.StartsWith.
- Gunakan kelebihan beban yang secara eksplisit menentukan aturan perbandingan string untuk operasi string. Biasanya, ini melibatkan pemanggilan metode kelebihan beban yang memiliki parameter jenis StringComparison.
- Gunakan StringComparison.Ordinal atau StringComparison.OrdinalIgnoreCase untuk perbandingan, sebagai default yang aman untuk pencocokan string yang agnostik budaya.
- Gunakan perbandingan dengan StringComparison.Ordinal atau StringComparison.OrdinalIgnoreCase untuk performa yang lebih baik.
- Gunakan operasi string yang didasarkan pada StringComparison.CurrentCulture saat Anda menampilkan output kepada pengguna.
- Gunakan nilai non-linguistik StringComparison.Ordinal atau StringComparison.OrdinalIgnoreCase alih-alih operasi string berdasarkan CultureInfo.InvariantCulture ketika perbandingannya tidak relevan secara linguistik (simbolis, misalnya).
- Gunakan metode String.ToUpperInvariant alih-alih metode String.ToLowerInvariant saat Anda menormalkan string untuk perbandingan.
- Gunakan kelebihan beban String.Equals metode untuk menguji apakah dua string sama.
- String.Compare Gunakan metode dan String.CompareTo untuk mengurutkan string, bukan untuk memeriksa kesetaraan.
- Gunakan pemformatan sensitif budaya untuk menampilkan data non-string, seperti angka dan tanggal, di antarmuka pengguna. Gunakan pemformatan dengan budaya invarian untuk mempertahankan data non-string dalam bentuk string.
Hindari praktik berikut saat Anda membandingkan string:
- Jangan gunakan kelebihan beban yang tidak secara eksplisit atau implisit menentukan aturan perbandingan string untuk operasi string.
- Jangan gunakan operasi string berdasarkan StringComparison.InvariantCulture dalam kebanyakan kasus. Salah satu dari beberapa pengecualian adalah ketika Anda menyimpan data yang bermakna secara linguistik tetapi netral secara budaya.
- Jangan gunakan kelebihan beban String.Compare metode atau CompareTo dan uji nilai pengembalian nol untuk menentukan apakah dua string sama.
Menentukan perbandingan string secara eksplisit
Sebagian besar metode manipulasi string di .NET kelebihan beban. Biasanya, satu atau beberapa overload menerima pengaturan default, sedangkan yang lain tidak menerima pengaturan default dan menentukan cara tepat untuk membandingkan atau memanipulasi string. Sebagian besar metode yang tidak bergantung pada default mencakup parameter jenis StringComparison, yang merupakan enumerasi yang secara eksplisit menentukan aturan untuk perbandingan string berdasarkan budaya dan kasus. Tabel berikut ini menjelaskan StringComparison anggota-anggota enumerasi.
| Anggota dari StringComparison | Deskripsi |
|---|---|
| CurrentCulture | Melakukan perbandingan peka huruf besar/kecil menggunakan budaya saat ini. |
| CurrentCultureIgnoreCase | Melakukan perbandingan tanpa membedakan huruf besar/kecil menggunakan kultur saat ini. |
| InvariantCulture | Melakukan perbandingan peka huruf besar/kecil menggunakan kultur invarian. |
| InvariantCultureIgnoreCase | Melakukan perbandingan yang tidak peka terhadap huruf besar/kecil menggunakan kultur invarian. |
| Ordinal | Melakukan perbandingan ordinal. |
| OrdinalIgnoreCase | Melakukan perbandingan ordinal yang tidak peka huruf besar/kecil. |
Misalnya, metode IndexOf, yang mengembalikan indeks dari substring dalam objek String yang cocok dengan karakter atau string, memiliki sembilan overload.
- IndexOf(Char), , IndexOf(Char, Int32)dan IndexOf(Char, Int32, Int32), yang secara default melakukan pencarian ordinal (peka huruf besar/kecil dan tidak peka terhadap budaya) untuk karakter dalam string.
- IndexOf(String), , IndexOf(String, Int32)dan IndexOf(String, Int32, Int32), yang secara default melakukan pencarian peka huruf besar/kecil dan sensitif terhadap budaya untuk substring dalam string.
- IndexOf(String, StringComparison), , IndexOf(String, Int32, StringComparison)dan IndexOf(String, Int32, Int32, StringComparison), yang mencakup parameter jenis StringComparison yang memungkinkan bentuk perbandingan ditentukan.
Kami menyarankan agar Anda memilih kelebihan beban yang tidak menggunakan nilai default, karena alasan berikut:
Beberapa overload dengan parameter default (yang mencari Char dalam instance string) melakukan perbandingan ordinal, sedangkan yang lain (yang mencari string dalam instance string) peka terhadap budaya. Sulit untuk mengingat metode mana yang menggunakan nilai default mana, dan mudah membingungkan kelebihan beban.
Niat kode yang bergantung pada nilai default untuk panggilan metode tidak jelas. Dalam contoh berikut, yang bergantung pada default, sulit untuk mengetahui apakah pengembang benar-benar bermaksud melakukan perbandingan ordinal atau linguistik dari dua string, atau apakah perbedaan kasus antara
url.Schemedan "https" dapat menyebabkan pengujian kesamaan mengembalikanfalse.Uri url = new("https://learn.microsoft.com/"); // Incorrect if (string.Equals(url.Scheme, "https")) { // ...Code to handle HTTPS protocol. }Dim url As New Uri("https://learn.microsoft.com/") ' Incorrect If String.Equals(url.Scheme, "https") Then ' ...Code to handle HTTPS protocol. End If
Secara umum, kami sarankan Anda memanggil metode yang tidak bergantung pada default, karena membuat niat kode tidak ambigu. Ini, pada gilirannya, membuat kode lebih mudah dibaca dan lebih mudah untuk di-debug dan dikelola. Contoh berikut membahas pertanyaan yang diajukan tentang contoh sebelumnya. Ini memperjelas bahwa perbandingan ordinal digunakan dan bahwa perbedaan jika diabaikan.
Uri url = new("https://learn.microsoft.com/");
// Correct
if (string.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
// ...Code to handle HTTPS protocol.
}
Dim url As New Uri("https://learn.microsoft.com/")
' Incorrect
If String.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase) Then
' ...Code to handle HTTPS protocol.
End If
Rincian perbandingan string
Perbandingan string adalah inti dari banyak operasi terkait string, terutama pengurutan dan pengujian untuk kesetaraan. String diurutkan dalam urutan yang ditentukan: Jika "my" muncul sebelum "string" dalam daftar string yang diurutkan, "my" harus dibandingkan lebih kecil atau sama dengan "string". Selain itu, perbandingan secara implisit mendefinisikan kesetaraan. Operasi perbandingan mengembalikan nol untuk string yang dianggap sama. Penafsiran yang baik adalah bahwa tidak ada string yang lebih rendah dari yang lainnya. Sebagian besar operasi yang bermakna yang melibatkan string mencakup satu atau kedua prosedur ini: membandingkan dengan string lain, dan menjalankan operasi pengurutan yang ditentukan dengan baik.
Nota
Anda dapat mengunduh Tabel Berat Pengurutan, sekumpulan file teks yang berisi informasi tentang bobot karakter yang digunakan dalam operasi pengurutan dan perbandingan untuk sistem operasi Windows, dan Tabel Elemen Kolase Unicode Default, versi terbaru tabel berat sortir untuk Linux dan macOS. Versi spesifik tabel berat sortir di Linux dan macOS bergantung pada versi pustaka Komponen Internasional Unicode yang diinstal pada sistem. Untuk informasi tentang versi ICU dan versi Unicode yang mereka terapkan, lihat Mengunduh ICU.
Namun, mengevaluasi dua string untuk kesetaraan atau urutan pengurutan tidak menghasilkan satu hasil yang benar; hasilnya tergantung pada kriteria yang digunakan untuk membandingkan string. Secara khusus, perbandingan string yang ordinal atau yang didasarkan pada penyusunan dan pengurutan budaya saat ini atau budaya invarian (budaya yang tidak terpaku pada lokal tertentu, tidak berdasarkan bahasa Inggris) dapat menghasilkan hasil yang berbeda.
Selain itu, perbandingan string menggunakan versi .NET yang berbeda atau menggunakan .NET pada sistem operasi atau versi sistem operasi yang berbeda dapat mengembalikan hasil yang berbeda. Untuk informasi selengkapnya, lihat String dan Standar Unicode.
Perbandingan string yang menggunakan budaya saat ini
Salah satu kriteria melibatkan penggunaan konvensi budaya saat ini saat membandingkan string. Perbandingan yang didasarkan pada budaya saat ini menggunakan budaya atau pengaturan lokal thread saat ini. Jika budaya tidak diatur oleh pengguna, budaya tersebut default ke pengaturan sistem operasi. Anda harus selalu menggunakan perbandingan yang didasarkan pada budaya saat ini ketika data relevan secara linguistik, dan ketika mencerminkan interaksi pengguna yang sensitif terhadap budaya.
Namun, perilaku perbandingan dan huruf besar-kecil dalam .NET berubah ketika kultur berubah. Ini terjadi ketika aplikasi dijalankan di komputer yang memiliki budaya yang berbeda dari komputer tempat aplikasi dikembangkan, atau ketika utas yang dieksekusi mengubah budayanya. Perilaku ini disengaja, tetapi tetap tidak jelas untuk banyak pengembang. Contoh berikut menggambarkan perbedaan urutan pengurutan antara budaya Inggris A.S. ("en-US") dan Swedia ("sv-SE"). Perhatikan bahwa kata "ångström", "Windows", dan "Visual Studio" muncul di posisi yang berbeda dalam array string yang diurutkan.
using System.Globalization;
// Words to sort
string[] values= { "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" };
// Current culture
Array.Sort(values);
DisplayArray(values);
// Change culture to Swedish (Sweden)
string originalCulture = CultureInfo.CurrentCulture.Name;
Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
Array.Sort(values);
DisplayArray(values);
// Restore the original culture
Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);
static void DisplayArray(string[] values)
{
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:");
foreach (string value in values)
Console.WriteLine($" {value}");
Console.WriteLine();
}
// The example displays the following output:
// Sorting using the en-US culture:
// able
// Æble
// ångström
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// apple
// Visual Studio
// Windows
// ångström
// Æble
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
' Words to sort
Dim values As String() = {"able", "ångström", "apple", "Æble",
"Windows", "Visual Studio"}
' Current culture
Array.Sort(values)
DisplayArray(values)
' Change culture to Swedish (Sweden)
Dim originalCulture As String = CultureInfo.CurrentCulture.Name
Thread.CurrentThread.CurrentCulture = New CultureInfo("sv-SE")
Array.Sort(values)
DisplayArray(values)
' Restore the original culture
Thread.CurrentThread.CurrentCulture = New CultureInfo(originalCulture)
End Sub
Sub DisplayArray(values As String())
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:")
For Each value As String In values
Console.WriteLine($" {value}")
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' Sorting using the en-US culture:
' able
' Æble
' ångström
' apple
' Visual Studio
' Windows
'
' Sorting using the sv-SE culture:
' able
' apple
' Visual Studio
' Windows
' ångström
' Æble
Perbandingan yang tidak membedakan huruf besar/kecil dan menggunakan budaya saat ini sama dengan perbandingan yang peka terhadap budaya, dengan pengecualian bahwa mereka mengabaikan perbedaan huruf besar/kecil sesuai dengan budaya utas saat ini. Perilaku ini juga dapat memanifestasikan dirinya dalam urutan pengurutan.
Perbandingan yang menggunakan semantik budaya saat ini adalah default untuk metode berikut:
- String.Compare kelebihan beban yang tidak menyertakan StringComparison parameter.
- String.CompareTo kelebihan beban.
- Metode default String.StartsWith(String), dan metode String.StartsWith(String, Boolean, CultureInfo) dengan parameter
nullCultureInfo. - Metode default String.EndsWith(String), dan metode String.EndsWith(String, Boolean, CultureInfo) dengan parameter
nullCultureInfo. - String.IndexOf kelebihan beban yang menerima String sebagai parameter pencarian dan yang tidak memiliki StringComparison parameter.
- String.LastIndexOf kelebihan beban yang menerima String sebagai parameter pencarian dan yang tidak memiliki StringComparison parameter.
Bagaimanapun, kami sarankan Anda memanggil overload yang memiliki StringComparison parameter untuk memperjelas maksud pemanggilan metode.
Bug yang halus dan tidak begitu halus dapat muncul ketika data string non-linguistik ditafsirkan secara linguistik, atau ketika data string dari budaya tertentu ditafsirkan menggunakan konvensi budaya lain. Contoh kanonis adalah masalah Turkish-I.
Untuk hampir semua alfabet Latin, termasuk bahasa Inggris A.S. , karakter "i" (\u0069) adalah versi huruf kecil dari karakter "I" (\u0049). Aturan penulisan ini dengan cepat menjadi standar bagi seseorang yang memprogram dalam lingkungan pemrograman seperti itu. Namun, alfabet Turki ("tr-TR") menyertakan karakter "I dengan titik" "İ" (\u0130), yang merupakan versi huruf besar dari "i". Turki juga menyertakan karakter "i tanpa titik" huruf kecil, "ı" (\u0131), yang menjadi huruf kapital "I". Perilaku ini juga terjadi pada budaya Azerbaijan ("az").
Oleh karena itu, asumsi yang dibuat tentang mengkapitalisasi "i" atau menghuruf kecilkan "I" tidak berlaku untuk semua budaya. Jika Anda menggunakan overload default untuk rutinitas perbandingan string, rutinitas tersebut akan dipengaruhi oleh perbedaan budaya. Jika data yang akan dibandingkan adalah non-linguistik, menggunakan overload default dapat menghasilkan hasil yang tidak diinginkan, seperti yang diperlihatkan oleh upaya berikut untuk melakukan perbandingan yang tidak peka terhadap besar-kecilnya huruf dari string "bill" dan "BILL".
using System.Globalization;
string name = "Bill";
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
Console.WriteLine();
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
//' The example displays the following output:
//'
//' Culture = English (United States)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? True
//'
//' Culture = Turkish (Türkiye)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? False
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
Dim name As String = "Bill"
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
Console.WriteLine()
Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
End Sub
End Module
' The example displays the following output:
'
' Culture = English (United States)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? True
'
' Culture = Turkish (Türkiye)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? False
Perbandingan ini dapat menyebabkan masalah signifikan jika budaya secara tidak sengaja digunakan dalam pengaturan sensitif keamanan, seperti dalam contoh berikut. Panggilan metode seperti IsFileURI("file:") mengembalikan true jika budaya saat ini adalah bahasa Inggris AS, tetapi false jika budaya saat ini adalah Turki. Dengan demikian, pada sistem Turki, seseorang dapat menghindari langkah-langkah keamanan yang memblokir akses ke URI yang tidak membedakan huruf besar-kecil yang dimulai dengan "FILE:".
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", true, null);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", True, Nothing)
End Function
Dalam hal ini, karena "file:" dimaksudkan untuk ditafsirkan sebagai pengidentifikasi non-linguistik, tidak peka budaya, kode harus ditulis seperti yang ditunjukkan dalam contoh berikut:
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)
End Function
Operasi string ordinal
Menentukan StringComparison.Ordinal atau nilai StringComparison.OrdinalIgnoreCase dalam panggilan metode menandakan perbandingan non-linguistik di mana karakteristik bahasa alami diabaikan. Metode yang dipanggil dengan nilai StringComparison ini mendasarkan keputusan operasi string pada perbandingan byte sederhana alih-alih tabel casing atau kesetaraan yang diparameterkan berdasarkan budaya. Dalam kebanyakan kasus, pendekatan ini paling sesuai dengan interpretasi string yang dimaksudkan sambil membuat kode lebih cepat dan lebih andal.
Perbandingan ordinal adalah perbandingan string di mana setiap byte dari setiap string dibandingkan tanpa interpretasi linguistik; misalnya, "windows" tidak cocok dengan "Windows". Ini pada dasarnya adalah panggilan ke fungsi runtime strcmp C. Gunakan perbandingan ini ketika konteks menentukan bahwa string harus dicocokkan dengan tepat atau menuntut kebijakan pencocokan konservatif. Selain itu, perbandingan ordinal adalah operasi perbandingan tercepat karena tidak menerapkan aturan linguistik saat menentukan hasil.
String di .NET dapat mengandung karakter null yang disematkan (dan karakter tidak tercetak lainnya). Salah satu perbedaan paling jelas antara perbandingan ordinal dan peka budaya (termasuk perbandingan yang menggunakan budaya invarian) menyangkut penanganan karakter null yang disematkan dalam string. Karakter ini diabaikan ketika Anda menggunakan metode String.Compare dan String.Equals untuk melakukan perbandingan yang peka terhadap budaya (termasuk perbandingan yang menggunakan budaya invariant). Akibatnya, string yang berisi karakter null yang disematkan dapat dianggap sama dengan string yang tidak. Karakter non-pencetakan yang disematkan mungkin dilewati untuk tujuan metode perbandingan string, seperti String.StartsWith.
Penting
Meskipun metode perbandingan string mengabaikan karakter null yang disematkan, metode pencarian string seperti String.Contains, , String.EndsWithString.IndexOf, String.LastIndexOf, dan String.StartsWith tidak.
Contoh berikut melakukan perbandingan sensitif budaya dari string "Aa" dengan string serupa yang berisi beberapa karakter null yang disematkan antara "A" dan "a", dan menunjukkan bagaimana kedua string dianggap sama:
string str1 = "Aa";
string str2 = "A" + new string('\u0000', 3) + "a";
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-us");
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Current Culture: {string.Compare(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Compare(str1, str2, StringComparison.InvariantCulture)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Current Culture: {string.Equals(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Equals(str1, str2, StringComparison.InvariantCulture)}");
string ShowBytes(string value)
{
string hexString = string.Empty;
for (int index = 0; index < value.Length; index++)
{
string result = Convert.ToInt32(value[index]).ToString("X4");
result = string.Concat(" ", result.Substring(0,2), " ", result.Substring(2, 2));
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Current Culture: 0
// Invariant Culture: 0
// With String.Equals:
// Current Culture: True
// Invariant Culture: True
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Current Culture: {String.Compare(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Compare(str1, str2, StringComparison.InvariantCulture)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Current Culture: {String.Equals(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Equals(str1, str2, StringComparison.InvariantCulture)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Current Culture: 0
' Invariant Culture: 0
' With String.Equals:
' Current Culture: True
' Invariant Culture: True
End Module
Namun, string tidak dianggap sama saat Anda menggunakan perbandingan ordinal, seperti yang ditunjukkan contoh berikut:
string str1 = "Aa";
string str2 = "A" + new String('\u0000', 3) + "a";
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Ordinal: {string.Compare(str1, str2, StringComparison.Ordinal)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Ordinal: {string.Equals(str1, str2, StringComparison.Ordinal)}");
string ShowBytes(string str)
{
string hexString = string.Empty;
for (int ctr = 0; ctr < str.Length; ctr++)
{
string result = Convert.ToInt32(str[ctr]).ToString("X4");
result = " " + result.Substring(0, 2) + " " + result.Substring(2, 2);
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Ordinal: 97
// With String.Equals:
// Ordinal: False
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Ordinal: {String.Compare(str1, str2, StringComparison.Ordinal)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Ordinal: {String.Equals(str1, str2, StringComparison.Ordinal)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Ordinal: 97
' With String.Equals:
' Ordinal: False
End Module
Perbandingan ordinal yang tidak peka huruf besar/kecil adalah pendekatan paling konservatif berikutnya. Perbandingan ini mengabaikan sebagian besar casing; misalnya, "windows" cocok dengan "Windows". Saat membahas karakter ASCII, kebijakan ini setara dengan StringComparison.Ordinal, kecuali bahwa kebijakan ini mengabaikan pengaturan huruf ASCII seperti biasanya. Oleh karena itu, setiap karakter dalam [A, Z] (\u0041-\u005A) cocok dengan karakter yang sesuai dalam [a,z] (\u0061-\007A). Casing di luar rentang ASCII menggunakan tabel budaya invariant. Oleh karena itu, perbandingan berikut:
string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase)
setara dengan (tetapi lebih cepat dari) perbandingan ini:
string.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal);
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal)
Perbandingan ini masih sangat cepat.
Baik StringComparison.Ordinal dan StringComparison.OrdinalIgnoreCase gunakan nilai biner secara langsung, dan paling cocok untuk pencocokan. Saat Anda tidak yakin tentang pengaturan perbandingan Anda, gunakan salah satu dari dua nilai ini. Namun, karena mereka melakukan perbandingan byte-byte, mereka tidak mengurutkan berdasarkan urutan sortir linguistik (seperti kamus bahasa Inggris) tetapi dengan urutan pengurutan biner. Hasilnya mungkin terlihat aneh di sebagian besar konteks jika ditampilkan kepada pengguna.
Semantik ordinal adalah default untuk String.Equals kelebihan beban yang tidak menyertakan StringComparison argumen (termasuk operator kesetaraan). Bagaimanapun, kami sarankan Anda memanggil fungsi overload yang memiliki parameter StringComparison.
Operasi string yang menggunakan kultur invarian
Perbandingan dengan budaya invarian menggunakan properti CompareInfo yang dikembalikan oleh properti statis CultureInfo.InvariantCulture. Perilaku ini sama pada semua sistem; ini menerjemahkan karakter apa pun di luar rentangnya ke dalam apa yang diyakininya adalah karakter invarian yang setara. Kebijakan ini dapat berguna untuk mempertahankan satu set perilaku string di seluruh budaya, tetapi sering memberikan hasil yang tidak terduga.
Perbandingan yang tidak peka huruf besar/kecil dengan budaya invarian juga menggunakan properti statis CompareInfo yang dikembalikan oleh properti statis CultureInfo.InvariantCulture untuk informasi perbandingan. Perbedaan kasus apa pun di antara karakter yang diterjemahkan ini diabaikan.
Perbandingan yang menggunakan StringComparison.InvariantCulture dan StringComparison.Ordinal bekerja secara identik pada string ASCII. Namun, StringComparison.InvariantCulture membuat keputusan linguistik yang mungkin tidak sesuai untuk string yang harus ditafsirkan sebagai sekumpulan byte. Objek CultureInfo.InvariantCulture.CompareInfo membuat metode Compare menginterpretasikan sekumpulan karakter tertentu sebagai setara. Misalnya, kesetaraan berikut berlaku di bawah budaya invarian:
Budaya Tidak Berubah: a + ̊ = å
HURUF KECIL LATIN Karakter "a" (\u0061), ketika berada di samping karakter COMBINING RING ABOVE "+ " ̊" (\u030a), ditafsirkan sebagai HURUF KECIL LATIN A DENGAN karakter RING DI ATAS "å" (\u00e5). Seperti yang ditunjukkan oleh contoh berikut, perilaku ini berbeda dari perbandingan ordinal.
string separated = "\u0061\u030a";
string combined = "\u00e5";
Console.WriteLine($"Equal sort weight of {separated} and {combined} using InvariantCulture: {string.Compare(separated, combined, StringComparison.InvariantCulture) == 0}");
Console.WriteLine($"Equal sort weight of {separated} and {combined} using Ordinal: {string.Compare(separated, combined, StringComparison.Ordinal) == 0}");
// The example displays the following output:
// Equal sort weight of a° and å using InvariantCulture: True
// Equal sort weight of a° and å using Ordinal: False
Module Program
Sub Main()
Dim separated As String = ChrW(&H61) & ChrW(&H30A)
Dim combined As String = ChrW(&HE5)
Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.InvariantCulture) = 0)
Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.Ordinal) = 0)
' The example displays the following output:
' Equal sort weight of a° and å using InvariantCulture: True
' Equal sort weight of a° and å using Ordinal: False
End Sub
End Module
Saat menafsirkan nama file, cookie, atau apa pun di mana kombinasi seperti "å" dapat muncul, perbandingan ordinal masih menawarkan perilaku yang paling transparan dan pas.
Secara keseluruhan, budaya invarian memiliki sedikit sifat yang membuatnya berguna untuk perbandingan. Ini melakukan perbandingan dengan cara yang relevan secara linguistik, yang mencegahnya menjamin kesetaraan simbolis penuh, tetapi tidak menjadi pilihan yang tepat untuk ditampilkan di budaya mana pun. Salah satu dari beberapa alasan untuk digunakan StringComparison.InvariantCulture sebagai perbandingan adalah untuk mempertahankan data yang dipesan untuk tampilan lintas budaya yang identik. Misalnya, jika file data besar yang berisi daftar pengidentifikasi yang diurutkan untuk tampilan menyertai aplikasi, menambahkan ke daftar ini akan memerlukan penyisipan dengan pengurutan gaya invariant.
Memilih anggota StringComparison untuk pemanggilan metode Anda
Tabel berikut menguraikan pemetaan dari konteks string semantik ke StringComparison anggota enumerasi:
| Data Informasi | Perilaku | Perbandingan System.String yang Sesuai nilai |
|---|---|---|
| Pengidentifikasi internal yang peka terhadap penggunaan huruf besar dan kecil. Pengidentifikasi peka huruf besar/kecil dalam standar seperti XML dan HTTP. Pengaturan keamanan yang sensitif terhadap huruf besar/kecil. |
Pengidentifikasi non-linguistik, di mana byte cocok persis. | Ordinal |
| Pengidentifikasi internal yang tidak sensitif huruf besar-kecil. Pengidentifikasi tidak peka huruf besar/kecil dalam standar seperti XML dan HTTP. Jalur file. Kunci dan nilai registri. Variabel lingkungan. Pengidentifikasi sumber daya (misalnya, menangani nama). Pengaturan terkait keamanan yang tidak peka huruf besar/kecil. |
Pengidentifikasi non-linguistik, di mana kasus tidak relevan. | OrdinalIgnoreCase |
| Beberapa data yang tetap ada dan relevan secara linguistik. Tampilan data linguistik yang memerlukan urutan pengurutan tetap. |
Data agnostik budaya yang masih relevan secara linguistik. | InvariantCulture -atau- InvariantCultureIgnoreCase |
| Data ditampilkan kepada pengguna. Sebagian besar input pengguna. |
Data yang memerlukan kebiasaan linguistik lokal. | CurrentCulture -atau- CurrentCultureIgnoreCase |
Metode perbandingan string umum di .NET
Bagian berikut menjelaskan metode yang paling umum digunakan untuk perbandingan string.
String.Compare
Interpretasi default: StringComparison.CurrentCulture.
Sebagai operasi yang paling penting untuk interpretasi string, semua panggilan metode ini harus diperiksa untuk menentukan apakah string harus ditafsirkan sesuai dengan budaya saat ini, atau dipisahkan dari konteks budaya (secara simbolis). Biasanya, ini yang terakhir, dan perbandingan StringComparison.Ordinal harus digunakan sebagai gantinya.
Kelas System.Globalization.CompareInfo, yang dikembalikan oleh properti CultureInfo.CompareInfo, juga mencakup metode Compare yang menyediakan sejumlah besar opsi pencocokan (ordinal, mengabaikan spasi, mengabaikan jenis kana, dan sebagainya) melalui enumerasi penanda CompareOptions.
String.CompareTo
Interpretasi default: StringComparison.CurrentCulture.
Metode ini saat ini tidak menawarkan kelebihan beban yang menentukan StringComparison jenis. Biasanya dimungkinkan untuk mengonversi metode ini ke formulir yang direkomendasikan String.Compare(String, String, StringComparison) .
Jenis yang mengimplementasikan IComparable antarmuka dan IComparable<T> menerapkan metode ini. Karena tidak menawarkan opsi StringComparison parameter, jenis penerapan sering kali memungkinkan pengguna menentukan StringComparer di konstruktor mereka. Contoh berikut mendefinisikan FileName kelas yang konstruktor kelasnya menyertakan StringComparer parameter. Objek ini StringComparer kemudian digunakan dalam FileName.CompareTo metode .
class FileName : IComparable
{
private readonly StringComparer _comparer;
public string Name { get; }
public FileName(string name, StringComparer? comparer)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
Name = name;
if (comparer != null)
_comparer = comparer;
else
_comparer = StringComparer.OrdinalIgnoreCase;
}
public int CompareTo(object? obj)
{
if (obj == null) return 1;
if (obj is not FileName)
return _comparer.Compare(Name, obj.ToString());
else
return _comparer.Compare(Name, ((FileName)obj).Name);
}
}
Class FileName
Implements IComparable
Private ReadOnly _comparer As StringComparer
Public ReadOnly Property Name As String
Public Sub New(name As String, comparer As StringComparer)
If (String.IsNullOrEmpty(name)) Then Throw New ArgumentNullException(NameOf(name))
Me.Name = name
If comparer IsNot Nothing Then
_comparer = comparer
Else
_comparer = StringComparer.OrdinalIgnoreCase
End If
End Sub
Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
If obj Is Nothing Then Return 1
If TypeOf obj IsNot FileName Then
Return _comparer.Compare(Name, obj.ToString())
Else
Return _comparer.Compare(Name, DirectCast(obj, FileName).Name)
End If
End Function
End Class
String.Equals
Interpretasi default: StringComparison.Ordinal.
Kelas String ini memungkinkan Anda menguji kesetaraan dengan memanggil overload metode statis atau instans Equals, atau dengan menggunakan operator kesetaraan statis. Kelebihan beban dan operator menggunakan perbandingan ordinal secara default. Namun, kami masih menyarankan agar Anda memanggil fungsi overload yang secara eksplisit menentukan jenis StringComparison, bahkan jika Anda ingin melakukan perbandingan ordinal; ini mempermudah pencarian kode untuk interpretasi string tertentu.
String.ToUpper dan String.ToLower
Interpretasi default: StringComparison.CurrentCulture.
Berhati-hatilah saat Anda menggunakan metode String.ToUpper() dan String.ToLower(), karena memaksa string ke huruf besar atau huruf kecil sering digunakan sebagai normalisasi kecil untuk membandingkan string terlepas dari besar-kecilnya huruf. Jika demikian, pertimbangkan untuk menggunakan perbandingan yang tidak peka huruf besar/kecil.
Metode String.ToUpperInvariant dan String.ToLowerInvariant juga tersedia. ToUpperInvariant adalah cara standar untuk menormalkan kasus. Perbandingan yang dilakukan menggunakan StringComparison.OrdinalIgnoreCase secara perilaku terdiri dari dua pemanggilan: memanggil ToUpperInvariant pada kedua argumen string, dan melakukan perbandingan menggunakan StringComparison.Ordinal.
Kelebihan beban juga tersedia untuk mengonversi ke huruf besar dan huruf kecil dalam budaya tertentu, dengan meneruskan CultureInfo objek yang mewakili budaya tersebut ke metode .
Char.ToUpper dan Char.ToLower
Interpretasi default: StringComparison.CurrentCulture.
Metode Char.ToUpper(Char) dan Char.ToLower(Char) berfungsi mirip dengan String.ToUpper() metode dan String.ToLower() yang dijelaskan di bagian sebelumnya.
String.StartsWith dan String.EndsWith (metode untuk menentukan awal dan akhir string)
Interpretasi default: StringComparison.CurrentCulture.
Secara default, kedua metode ini melakukan perbandingan yang sensitif terhadap budaya. Khususnya, mereka dapat mengabaikan karakter yang tidak dapat dicetak.
String.IndexOf dan String.LastIndexOf
Interpretasi default: StringComparison.CurrentCulture.
Ada kurangnya konsistensi dalam bagaimana kelebihan beban default metode ini melakukan perbandingan. Semua metode String.IndexOf dan String.LastIndexOf yang menyertakan parameter Char melakukan perbandingan ordinal, tetapi metode default String.IndexOf dan String.LastIndexOf yang menyertakan parameter String melakukan perbandingan yang sensitif terhadap budaya.
Jika Anda memanggil String.IndexOf(String) metode atau String.LastIndexOf(String) dan meneruskannya string untuk menemukan dalam instans saat ini, kami sarankan Anda memanggil kelebihan beban yang secara eksplisit menentukan jenisnya StringComparison . Kelebihan beban yang menyertakan Char argumen tidak memungkinkan Anda menentukan StringComparison jenis.
Metode yang melakukan perbandingan string secara tidak langsung
Beberapa metode non-string yang memiliki perbandingan string sebagai operasi pusat menggunakan jenis .StringComparer Kelas StringComparer mencakup enam properti statis yang mengembalikan instance StringComparer yang metodenya StringComparer.Compare melakukan jenis perbandingan string berikut:
- Perbandingan string yang sensitif terhadap budaya menggunakan budaya saat ini. Objek StringComparer ini dikembalikan oleh properti StringComparer.CurrentCulture.
- Perbandingan yang tidak peka terhadap huruf besar/kecil menggunakan kultur saat ini. Objek StringComparer ini dikembalikan oleh properti StringComparer.CurrentCultureIgnoreCase.
- Perbandingan budaya-tidak sensitif menggunakan aturan perbandingan kata dari budaya invarian. Objek StringComparer ini dikembalikan oleh properti StringComparer.InvariantCulture.
- Perbandingan yang tidak peka huruf besar/kecil dan tidak peka terhadap budaya menggunakan aturan perbandingan kata dari budaya invarian. Objek StringComparer ini dikembalikan oleh properti StringComparer.InvariantCultureIgnoreCase.
- Perbandingan ordinal. Objek StringComparer ini dikembalikan oleh properti StringComparer.Ordinal.
- Perbandingan ordinal yang tidak membedakan huruf besar-kecil. Objek StringComparer ini dikembalikan oleh properti StringComparer.OrdinalIgnoreCase.
Array.Sort dan Array.BinarySearch
Interpretasi default: StringComparison.CurrentCulture.
Saat Anda menyimpan data apa pun dalam koleksi, atau membaca data yang bertahan dari file atau database ke dalam koleksi, mengalihkan budaya saat ini dapat membatalkan invarian dalam koleksi. Metode ini Array.BinarySearch mengasumsikan bahwa elemen dalam array yang akan dicari sudah diurutkan. Untuk mengurutkan elemen string apa pun dalam array, Array.Sort metode memanggil String.Compare metode untuk mengurutkan elemen individual. Menggunakan pembanding yang sensitif terhadap pengaturan lokal dapat berbahaya jika pengaturan lokal berubah antara waktu array diurutkan dan isi array tersebut dicari. Misalnya, dalam kode, penyimpanan, dan pengambilan berikut beroperasi pada comparer yang disediakan secara implisit oleh Thread.CurrentThread.CurrentCulture properti . Jika budaya dapat berubah antara panggilan ke StoreNames dan DoesNameExist, dan terutama jika konten array disimpan di suatu tempat antara dua panggilan metode, pencarian biner mungkin gagal.
// Incorrect
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name) >= 0; // Line B
' Incorrect
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name) >= 0 ' Line B
End Function
Variasi yang direkomendasikan muncul dalam contoh berikut, yang menggunakan metode perbandingan ordinal (tidak peka budaya) yang sama baik untuk mengurutkan maupun untuk mencari array. Kode perubahan tercermin dalam baris berlabel Line A dan Line B dalam dua contoh.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.Ordinal); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.Ordinal) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0 ' Line B
End Function
Jika data ini bertahan dan dipindahkan di seluruh budaya, dan pengurutan digunakan untuk menyajikan data ini kepada pengguna, Anda mungkin mempertimbangkan untuk menggunakan StringComparison.InvariantCulture, yang beroperasi secara linguistik untuk output pengguna yang lebih baik tetapi tidak terpengaruh oleh perubahan budaya. Contoh berikut memodifikasi dua contoh sebelumnya untuk menggunakan budaya invarian untuk mengurutkan dan mencari array.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.InvariantCulture); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.InvariantCulture) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0 ' Line B
End Function
Contoh Koleksi: Konstruktor Hashtable
String hashing menyediakan contoh kedua operasi yang dipengaruhi oleh cara string dibandingkan.
Contoh berikut menginstansiasi objek Hashtable dengan meneruskan objek StringComparer yang dikembalikan oleh properti StringComparer.OrdinalIgnoreCase. Karena kelas StringComparer yang berasal dari StringComparer mengimplementasikan IEqualityComparer antarmuka, metodenya GetHashCode digunakan untuk menghitung kode hash string dalam tabel hash.
using System.IO;
using System.Collections;
const int InitialCapacity = 100;
Hashtable creationTimeByFile = new(InitialCapacity, StringComparer.OrdinalIgnoreCase);
string directoryToProcess = Directory.GetCurrentDirectory();
// Fill the hash table
PopulateFileTable(directoryToProcess);
// Get some of the files and try to find them with upper cased names
foreach (var file in Directory.GetFiles(directoryToProcess))
PrintCreationTime(file.ToUpper());
void PopulateFileTable(string directory)
{
foreach (string file in Directory.GetFiles(directory))
creationTimeByFile.Add(file, File.GetCreationTime(file));
}
void PrintCreationTime(string targetFile)
{
object? dt = creationTimeByFile[targetFile];
if (dt is DateTime value)
Console.WriteLine($"File {targetFile} was created at time {value}.");
else
Console.WriteLine($"File {targetFile} does not exist.");
}
Imports System.IO
Module Program
Const InitialCapacity As Integer = 100
Private ReadOnly s_creationTimeByFile As New Hashtable(InitialCapacity, StringComparer.OrdinalIgnoreCase)
Private ReadOnly s_directoryToProcess As String = Directory.GetCurrentDirectory()
Sub Main()
' Fill the hash table
PopulateFileTable(s_directoryToProcess)
' Get some of the files and try to find them with upper cased names
For Each File As String In Directory.GetFiles(s_directoryToProcess)
PrintCreationTime(File.ToUpper())
Next
End Sub
Sub PopulateFileTable(directoryPath As String)
For Each file As String In Directory.GetFiles(directoryPath)
s_creationTimeByFile.Add(file, IO.File.GetCreationTime(file))
Next
End Sub
Sub PrintCreationTime(targetFile As String)
Dim dt As Object = s_creationTimeByFile(targetFile)
If TypeOf dt Is Date Then
Console.WriteLine($"File {targetFile} was created at time {DirectCast(dt, Date)}.")
Else
Console.WriteLine($"File {targetFile} does not exist.")
End If
End Sub
End Module