C# String İşlemleri ve Düzenli İfadeler

Merhaba, bu dersimizde string veri tipine özel işlemlerden ve string içerisinde karmaşık aramalar yapmamıza imkan sağlayan düzenli ifadelerden bahsedeceğiz. Öncelikle string veri tipinin ne olduğuna değineceğiz. Ardından String sınıfındaki statik metotlara bakacağız. Sonrasında string veri tipine ait bazı extension metotları inceleyeceğiz. Son olarak da düzenli ifadelerden ve extension metot yazımından bahsedeceğiz.

DERS 14 : String İşlemleri ve Düzenli İfadeler

String Nedir?

Yazdığımız uygulamalarda bir çok yerde metinler kullanmamız gerekir. Kullanıcıdan değer isterken, kullanıcıya bir sonuç gösterirken, metin dosyasından okuma yaptığımızda veya metin dosyasına yazdığımızda hep string veri tipini kullanırız. String, temelde karakterlerin bir araya gelmesiyle oluşan veri tipidir. Bellek düzeyinde char[] yani karakter dizisi olarak saklanır. Aşağıdaki örnekte iki farklı değişken aslında aynı şeyi ifade eder.

C#
static void Main(string[] args)
{
    string stringVariable = "Kemal";
    char[] charArray = new char[] { 'K', 'e', 'm', 'a', 'l' };

    Console.WriteLine(stringVariable[0] == charArray[0]);
    Console.ReadKey();
}

Örnekte de gördüğünüz gibi, string değişkenlerini bir dizi gibi kullanabilirsiniz. stringVariable değişkeni ile charArray değişkeninin sıfırıncı elemanları aynı olduğu için programın çıktısı True olacaktır.

String Sınıfı

String sınıfı içerisinde, birçok static metot bulunmakta. Bu metotlardan bazıları ise extension olarak de kullanılabilir durumda. Extension; herhangi bir değişken isminden sonra . (nokta) koyarak erişebildiğiniz metotlara verilen isimdir. İsterseniz siz de extension metotlar yazabilirsiniz. Extension metotların nasıl yazıldığını makalenin sonunda anlatacağım.

String sınıfı içerisindeki bazı metotlar ve ürettikleri çıktıları aşağıda görebilirsiniz:

C#
static void Main(string[] args)
{
    string name = "Kemal";
    string surname = "Kefeli";
    string[] numbers = new string[] { "1", "2", "3" };

    //Compare: ilk değişken ikinciden önce geliyorsa -1,
    //sonra geliyorsa 1, eşitse 0 döner
    Console.WriteLine(String.Compare(name, surname)); // 1

    //Concat: aldığı parametreleri birleştirir
    Console.WriteLine(String.Concat(name, " ", surname)); // Kemal Kefeli

    //Empty: boş string için tanımlanmış field
    Console.WriteLine("" == String.Empty); // True

    //Format: {0}, {1} gibi alanlara sırasıyla değişkenleri
    //koyarak formatlama yapar
    Console.WriteLine(String.Format("Yazar: {0} {1}", name, surname)); // Yazar: Kemal Kefeli
    //.Net 4.6 ve sonrasında kısaltılmış yazım şekli kullanılabilir
    Console.WriteLine($"Yazar: {name} {surname}"); // Yazar: Kemal Kefeli

    //IsNullOrEmpty: Değişken null veya boş string olup olmadığına bakar
    Console.WriteLine(String.IsNullOrEmpty(name)); // False

    //Join: verilen dizi elemanlarını verilen ayraçla birleştirir
    Console.WriteLine(String.Join(",", numbers)); // 1,2,3

    Console.ReadKey();
}

String değişkenlerine ait extension metotlar ve ürettikleri çıktılar ise aşağıdaki gibi:

C#
static void Main(string[] args)
{
    string surname = "Kefeli";
    string spaces = "   K   ";

    //Contains: değişkenin içinde arama yapar
    Console.WriteLine(surname.Contains("efe")); //True

    //EndsWith: değişken, belirtilen parametreyle bitiyor mu diye bakar
    Console.WriteLine(surname.EndsWith("li")); //True

    //IndexOf: belirtilen parametrenin değişken içerisindeki İLK konumunu döner
    //bulamazsa -1 döner
    Console.WriteLine(surname.IndexOf('e')); // 1

    //LastIndexOf: belirtilen parametrenin değişken içerisindeki SON konumunu döner
    //bulamazsa -1 döner
    Console.WriteLine(surname.LastIndexOf('e')); // 3

    //Split: değişkeni verilen parametreden böler
    string[] parts = surname.Split('e'); // parts[0]: K, parts[1]:f, parts[2]:li

    //Substring: değişkenin içerisinden belirtilen parametrelere göre parça alır
    Console.WriteLine(surname.Substring(1, 3)); // efe

    //ToLower: harflerin tamamını küçük harf yapar
    Console.WriteLine(surname.ToLower()); // kefeli

    //ToUpper: harflerin tamamını büyük harf yapar
    Console.WriteLine(surname.ToUpper()); // KEFELİ

    //Trim: değişkenin başındaki ve sonundaki boşlukları siler
    //parametre veirilirse boşluk yerine o karakterleri siler
    Console.WriteLine(spaces.Trim()); // K

    Console.ReadKey();
}
String Birleştirme

C# dilinde, string birleştirmenin birden fazla yolu vardır. Herkesin bildiği ve en kolay olanı ise + operatörünü kullanarak birleştirmektir. Bunun haricinde yukarıdaki örneklerde iki kullanım daha mevcut: String.Concat ve String.Format. Hatta string birleştirmek için ayrı bir sınıf bile mevcut: StringBuilder. Şimdi bunların kullanımlarını ve hangi durumlarda tercih edilmesi gerektiğini inceleyelim.

C#
static void Main(string[] args)
{
    string name = "Kemal";
    string surname = "Kefeli";

    Console.WriteLine(name + " " + surname);

    Console.WriteLine(String.Concat(name, " ", surname));

    Console.WriteLine(String.Format("{0} {1}", name, surname));

    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(name);
    sb.Append(" ");
    sb.Append(surname);
    Console.WriteLine(sb.ToString());

    Console.ReadKey();
}

Yukarıda gördüğünüz gibi 4 farklı yöntem mevcut. Hepsinin çıktısı da aynı. Peki neden bu kadar fazla yöntem var? Çünkü hepsinin kullanılması gereken yer farklı.

  • “+” operatörü: String birleştirirken bu operatör iki veya en fazla üç değişkeni birleştirirken kullanılmalıdır. Üçten fazla kullanımında performansı düşük olacaktır.
  • Concat metodu: 2-8 arası değişken olduğunda bu metot kullanılabilir. Daha fazlası için yine performansı düşük olacaktır.
  • Format metodu: Bu metot daha çok formatlama amaçlı kullanılmalıdır. Örneğin elinizde bir cümle varsa ve bu cümlenin bazı yerlerine değişkenler gelecekse bu metot kullanılabilir.
  • StringBuilder sınıfı: Çok sayıda değişken birleştirilecekse mutlaka bu sınıf kullanılmalıdır. Örneğin bir döngü içerisinde string oluşturuyorsanız mutlaka StringBuilder kullanmalısınız.

Yöntemler arasındaki performans farkları elbette ki çok devasa farklar değil; ancak amacına uygun kullanmak her zaman için daha iyi olacaktır. Peki nasıl bir performans farkından bahsediyoruz? Yapmış olduğum basit bir performans testinin sonuçları aşağıdaki gibi. Testin kaynak kodunu makalenin sonunda bulabilirsiniz.

  • Kelime sayısı: 34730
  • “+” operatörü: 2151 ms
  • Concat metodu: 1927 ms
  • Format metodu: 5121 ms
  • Builder sınıfı: 1 ms

Gördüğünüz gibi, kelime sayısı çok fazlaysa StringBuilder sınıfı açık ara önde bitiriyor. Bu seçeneklerin yanında, eğer benim testte yaptığım gibi dizi içerisindeki string’leri birleştirecekseniz String.Join metodunu da kullanabilirsiniz. StringBuilder’dan çok az daha performanslı çalışabiliyor.

Düzenli İfadeler

Düzenli ifadeler (diğer bir deyişle Regular Expressions); bir kelime, cümle veya büyük bir metin içerisinde arama yapmak için kullanılan bir çeşit özel yazım formatıdır. Düzenli ifadeler ile, bir string içerisindeki istediğiniz şablona uygun kısımları bulabilirsiniz. Örneğin, bir metin içerisindeki tüm araç plakalarını (ör: 00AA000 formatında) bulmak istiyorsanız, düzenli ifadeleri kullanabilirsiniz. Düzenli ifadeler çok geniş bir konu. Kendine özel bir çok kuralı mevcut. Bir örnekle giriş yapalım:

Not: Regex sınıfını kullanabilmek için System.Text.RegularExpressions namespace’ini using ifadesiyle eklemeniz gerekiyor.

C#
static void Main(string[] args)
{
    string input = @"Bugün 34KL123 plakalı bir araç gördüm.
    Aracın arkasında 78AL456 plakalı başka bir araç vardı.";

    string pattern = @"\d{2}[A-Z]{2}\d{3}";
    Regex regex = new Regex(pattern);

    MatchCollection matches = regex.Matches(input);

    foreach (Match match in matches)
    {
        Console.WriteLine(match.Value);
    }

    Console.ReadKey();
}

Yukarıdaki örnekte içerisinde iki plaka bulunan bir metin yazdım. Hemen altında pattern isimli değişkene, nasıl bir arama şablonu istediğimi belirttim ve bu şablonu Regex sınıfının constructor’ına geçtim. Sonrasında regex.Matches metoduyla metin içerisinde eşleşen değerleri aldım. Değerleri ekrana yazdırmak için de foreach döngüsünü kullandım. Eğer size değerler lazım değilse, sadece metnin içinde var mı yok mu kontrolü yapmak istiyorsanız regex.Matches yerine reges.IsMatch metodunu kullanabilirsiniz. Eğer şablonla uyuşan bir değer bulunursa bu metot size True değerini dönecektir. Şablonlar haricinde geri kalan kısım aşağı yukarı tüm düzenli ifadelerde aynıdır, yani tüm işler için aynı kod bloğunu kullanabilirsiniz.

Şimdi biraz şablonlardan bahsedelim. Şablonlar, Regex arama motoruna nasıl bir şey araması gerektiğini bildirir. Örneğin benim yazmış olduğum şablonu ele alırsak “\d{2}[A-Z]{2}\d{3}” ifadesinde \d ifadesi arama motoruna sayı araması gerektiğini bildirir. Yani 0 ile 9 arasındaki değerlerle eşleşmeye çalışır. Yanındaki {2} ifadesi ise, 2 adet yan yana olan sayıları bul demektir (yani il kodu kısmı). Sonrasındaki parçada ise [A-Z] yazan yer, “A’dan Z’ye kadar olan harflerden herhangi biri” demektir. Burada dikkat edilmesi gereken iki nokta var: birincisi A’dan Z’ye derken, İngiliz alfabesi kastediliyor. Yani ç, ö, ş gibi ifadelerle eşleşme sağlanmaz. Onları ayrıca belirtmek gerekir. İkincisi ise A-Z yazdığımız için sadece büyük harflerle eşleşir. Küçük harflerle de eşleşsin istiyorsak ya a-z de yazmamız gerekir ya da new Regex(pattern) yazdığımız yerde ikinci bir parametre ile “büyük-küçük harf farklılığını göz ardı et” demeliyiz. Onu da şu şekilde ifade ediyoruz: new Regex(pattern, RegexOptions.IgnoreCase). {2} ifadesi ise, deminki gibi 2 tane harf demektir. Son parçada ise 3 tane sayı yer alıyor. Yani özetle, 2 sayı 2 harf ve 3 sayı şeklinde olan tüm metinleri bul demiş oluyoruz.

Aşağıda özel regex karakterlerini göstermeye çalıştım:

  • Birçok yerde olduğu gibi burada da \ karakteri yanına gelen harfe özel bir anlam katar. Eğer şablon içerisinde \ karakterini kullanmak isterseniz \\ şeklinde yazmanız gerekir.
  • \t: Tab karakterini arar.
  • \w: Herhangi bir harf veya sayı karakterini arar [A-Za-z0-9] ile aynı anlama gelir.
  • \W: Herhangi harf ve sayı olmayan karakteri arar. “.”, “,” gibi.
  • \s: Boşluk karakterini arar.
  • \S: Boşluk, harf ve sayı harici karakteri arar.
  • \d: Sayı olan karakteri arar.
  • \D: Sayı harici tüm karakterleri arar.
  • ^: Metnin başlangıcını ifade eder.
  • $: Metnin sonunu ifade eder.
  • *: Kendinden önceki ifadenin 0 veya daha fazla olabileceğini belirtir.
  • +: Kendinden önceki ifadenin 1 veya daha fazla olabileceğini belirtir.
  • ?: Kendinden önceki ifadenin 0 veya 1 defa olabileceğini belirtir.
  • {n}: Kendinden önceki ifadenin n defa olabileceğini belirtir.
  • {n,m}: Kendinden önceki ifadenin en az n en fazla m defa olabileceğini belirtir.
  • {n,}: Kendinden önceki ifadenin en az n defa olabileceğini belirtir.
  • |: Kendinden önceki veya sonraki ifadeden birinin olabileceğini belirtir.
  • []: Dizi ya da aralığı belirtir. [A-Z] veya [ABC] gibi.
  • .: Herhangi bir karakteri belirtir.
  • (): Gruplama yapılmasını sağlar. Kelime belirtmek için de kullanılabilir.

Düzenli ifadelerle ilgili daha fazla bilgi ve örnek için aşağıdaki linklerden faydalanabilirsiniz:

Extension Metot Yazımı

Extension metodun normal metottan çok fazla bir farkı yok aslında. Sadece 3 şeye dikkat etmek gerekiyor:

  • Extension metotları yazmak için oluşturacağınız sınıfın namespace tanımı olmamalı (zorunlu değil ama, eğer olursa extension metodu kullanabilmek için using eklemeniz gerekir).
  • Bu yeni oluşturacağınız sınıf ve içine yazacağınız tüm metotlar static olmalı.
  • Yazacağınız metodun ilk parametresi this ile başlamalı ve extension metodun uygulanacağı veri tipinde olmalı.

Örnek olarak, birkaç extension metot yazalım. Yeni bir konsol uygulaması oluşturalım ve projeye Extensions.cs isimli bir sınıf ekleyelim. Bu sınıfın içeriği şöyle olsun:

C#
using System;

public static class Extensions
{
    public static Boolean IsLongString(this string input)
    {
        return input.Length > 40;
    }

    public static int ToInteger(this string input, int defaultValue = 0)
    {
        int value;
        if (int.TryParse(input, out value))
        {
            return value;
        }
        else
        {
            return defaultValue;
        }
    }

    public static int ToAge(this DateTime input)
    {
        return (int)((DateTime.Today - input).TotalDays / 365);
    }
}

Gördüğünüz gibi, sınıfın içerisinde namespace tanımı yok. Ayrıca static bir sınıf ve tüm metotları static. 3. maddeyi bu örnek metotlarla daha iyi anlatabilirim. Örneğin string tipi için bir extension metot yazmak istiyorsanız, ilk iki metotta olduğu gibi, metodun ilk parametresi this string degiskenAdi şeklinde olmalı. DateTime tipi için bir extension yazmak istiyorsanız this DateTime degiskenAdi şeklinde olmalı. this object degiskenAdi şeklinde bir metot yazarsanız, bu extension metot tüm veri tipleri için geçerli olur. Örnek uygulamamıza geri dönelim ve Program sınıfımızın içerisindeki Main metodunu da aşağıdaki şekilde güncelleyelim:

C#
static void Main(string[] args)
{
    string fullName = "Kemal Kefeli";
    string number = "5";
    DateTime birthDay = new DateTime(1992, 3, 1);

    Console.WriteLine(fullName.IsLongString()); // False

    Console.WriteLine(fullName.ToInteger(3)); // 3

    Console.WriteLine(number.ToInteger()); // 5

    Console.WriteLine(birthDay.ToAge().ToString()); // 24

    Console.ReadKey();
}

Yazmış olduğumuz tüm extension metotları uygulamamızda kullandık. Gördüğünüz gibi extension metot yazmak normal metot yazmaktan çok da farklı değil. Belirttiğim gibi 3 noktaya dikkat etmeniz yeterli.

Böylelikle bu makalenin sonuna gelmiş oluyoruz. Bir başka makalede görüşmek üzere…

C# Ders 14
C# Ders 14
Size: 42 KB

Benzer Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

warning
www.kemalkefeli.com.tr üzerindeki herhangi bir yazının veya kodun izinsiz olarak başka bir yerde kullanılması yasaktır.