11.Regular Expressions
Regular Expressions = Düzenli İfadeler
Düzenli ifadeler matematikten programlamaya aktarılmış bir kavramdır. Bir yazı içerisinde düzenli ifade formatına uygun olan alt yazıyı bulmaya yarar. Düzenli ifadeler kendi içerisinde bir takım özel karakterlere sahiptir. Bu karakterler ile bir düzenli ifade oluşturulur ve bu düzenli ifade içerisinde belirli koşula uygun olan yazı elde edilir. Düzenli ifade ayrıştıran programları yazmak zordur. Bu programlara genel RegEx Engine(Düzenli İfade Motorları) denilmektedir. Düzenli ifadeler için herhangi bir standart yoktur. Bir çok programlama ortamında desteklenir, belirli bir standart olmamasına karşın birbirlerine çok benzemektedir.
Java'da düzenli ifadeler için temel olarak 3 tane sınıf kullanılmaktadır:
- matcher
- pattern
- pattern syntax exception
Bu sınıflar java.util.regex paketi içerisinde bulunmaktadır. Bu sınıfların başlangıç metotları yoktur.
/*----------------------------------------------------------------------------------------------------------------------
Bir yazı içerisindeki sayıları düzenli ifade ile elde eden program
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String regex = "[0-9]+";
String text = "Bugün hava 30 derece, dün 25 dereceydi";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(text);
while (m.find()) {
System.out.println(text.substring(m.start(), m.end()));
}
}
}
/*----------------------------------------------------------------------------------------------------------------------
Yukarıdaki işlemin bir eşdeğeri
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String regex = "[^a-zA-Züğçş, ]+";
String text = "Bugün hava 30 derece, dün 125 dereceydi";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(text);
while (m.find()) {
System.out.println(text.substring(m.start(), m.end()));
}
}
}
/*----------------------------------------------------------------------------------------------------------------------
Tarihe ilişkin bir düzenli ifade
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String regex = "\\d{1,}/\\d{1,}/\\d{2,}";
String text = "Ben 10/9/1976 yılında doğdum. Kardeşim 12/12/77 yılında doğdu";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(text);
while (m.find()) {
System.out.println(text.substring(m.start(), m.end()));
}
}
}
/*----------------------------------------------------------------------------------------------------------------------
Basit bir email adres düzenli ifadesi
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String regex = "[a-z0-9]+@[a-z0-9]+\\.[a-z]+";
String text = "Mail adresin [email protected], eski mail adresim [email protected] dur";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(text);
while (m.find()) {
System.out.println(text.substring(m.start(), m.end()));
}
}
}
Düzenli ifadeler için özel bazı semboller bulunmaktadır. Düzenli ifadelere ilişkin bu sembollerin birleşimine kalıp(pattern) denilmektedir. Bu kalıplar çeşitli şekillerde oluşturulabilir.
Düzenli İfadelerde Kalıpların Oluşturulması
Düzenli ifadelerde kalıplar çeşitli şekillerde oluşturulabilir
Karakter Sınıfları (Character Classes)
Karakter sınıfları köşeli parantez içerisinde kalan karakterleri temsil etmektedir. Örnek karakter sınıfları şunlar olabilir;
Meta Karakterler
Düzenli ifadeler için aşağıdaki karakterlerin özel bir anlamı vardır, eğer bu karakterler özel anlamı dışında kullanılmak istiyorlarsa "^" işareti kullanılmalıdır.
<([{\^-=$!|]})?*+.>
Sınıf Çalışması:
Adı soyadı ve email adresi bir form ile girilen bir uygulamasında ad soyad bilgisinin ve email adresi bilgisinin geçerliliğini Servlet tarafında kontrol eden ve duruma göre mesaj gönderen programı yazınız.
Örnek karakter sınıfları:
Karakter | Açıklama |
---|---|
[0-9] | 0 ile 9 arasındaki rakamlardan herhangi birisi varsa al |
[a-zA-Z] | Büyük veya küçük harf olan herhangi bir İngilizce alfabedeki karakter |
[a-zA-ZçğıöşüÇĞIOÖŞÜ] | Büyük veya küçük harf olan herhangi bir Türkçe alfabedeki karakter |
Bir düzenli ifadede doğrudan bulunan bir karakter özel bir anlam ifade etmiyor ise kalıp içerisine doğrudan dahil edilmiştir. Örn: [a-zA-Z]@
Anahtar Notlar: Anımsanacağı gibi ingillizce alfabedeki 22 karakterin büyük ve küçük olanları standart UNICODE tablosunun ilk 128 karakterine belirli bir sıraya göre serpiştirilmiştir. Fakat Türkçe karakterler ilk 128 içerisinde değildir ve standart olarak kabul edilmez. Bu durumda programcı bir düzenli ifadede Türkçe karakterleri de bulmak istiyorsa karakter kümesine onları da eklemelidir.
Sınıf Çalışması
Klavyeden girilen bir yazı içerisinde mail adreslerine ilişkin "@" karakterinden öncesi ve domain ismini ayrı ayrı elde eden programı yazınız.
/*----------------------------------------------------------------------------------------------------------------------
Bir yazı içerisinde mail adreslerine ilişkin @ karakteri öncesi ve
sonrasını elde eden program
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String regex = "[a-zA-Z0-9.]+";
String text = "[email protected]";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(text);
while (m.find()) {
System.out.println(text.substring(m.start(), m.end()));
}
}
}
Sınıf Çalışması
Parametresiyle aldığı bir yazının Java'da geçerli bir değişken ismi olup olmadığını kontrol eden isIdentifier isimli metodu düzenli ifade kullanarak yazınız ve test ediniz.
/*----------------------------------------------------------------------------------------------------------------------
Sınıf Çalışması: Parametresi ile aldığı bir yazının
Java'da geçersi bir değişken ismi olup olmadığını kontrol eden
isVariable isimli metodu yazınız ve test ediniz
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.csystem.utils.Keyboard;
public class App {
public static void main(String[] args)
{
Keyboard kb = new Keyboard();
String name;
while (!(name = kb.getLine("İsim?")).equals("exit")) {
System.out.println(Utils.isVariable(name) ? "Geçerli" : "Geçersiz");
}
}
}
class Utils {
public static boolean isVariable(String s)
{
String patternStr = "^[a-z_A-Z][a-zA-Z0-9]*$";
Pattern pattern = Pattern.compile(patternStr);
Matcher m = pattern.matcher(s);
int count = 0;
while (m.find()) {
count++;
if (count == 2)
break;
}
return count == 1;
}
}
/*----------------------------------------------------------------------------------------------------------------------
Yukarıdaki sınıf çalışmasında değişken isimlerinde Türkçe karakterler de
kullanılabilen versiyon
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.csystem.utils.Keyboard;
public class App {
public static void main(String[] args)
{
Keyboard kb = new Keyboard();
String name;
while (!(name = kb.getLine("İsim?")).equals("exit")) {
System.out.println(Utils.isVariable(name) ? "Geçerli" : "Geçersiz");
}
}
}
class Utils {
public static boolean isVariable(String s)
{
String patternStr = "^[a-z_A-ZçÇğĞıİöÖüÜ][a-zA-Z0-9çÇğĞıİöÖüÜ]*$";
Pattern pattern = Pattern.compile(patternStr);
Matcher m = pattern.matcher(s);
int count = 0;
while (m.find()) {
count++;
if (count == 2)
break;
}
return count == 1;
}
}
Examples.txt'den örnekler;
/*----------------------------------------------------------------------------------------------------------------------
Sınıf Çalışması: String sınıfının split metoduna kolay bir kullanım örneği
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
public class App {
public static void main(String[] args)
{
String str = "Bugün hava :;?çok güzel";
for (String s : Utils.split(str, " :\t;?", StringSplitOptions.REMOVEEMPTYENTRIES))
System.out.println(s);
}
}
enum StringSplitOptions {NONE, REMOVEEMPTYENTRIES}
class Utils {
public static String [] split(String s, String delims, StringSplitOptions op)
{
if (s == null || delims == null || delims.isEmpty() || op == null)
throw new IllegalArgumentException("Invalid arguments");
String regex = "[" + delims + "]";
if (op == StringSplitOptions.REMOVEEMPTYENTRIES)
regex += "+";
return s.split(regex);
}
}
/*----------------------------------------------------------------------------------------------------------------------
Karakter sabitlerinin \u ile yazılmasının regex için kullanımı
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String str = "1Ö223Ö";
String pattern = "^[a-z" + '\u00D6' + "0-9]+$";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(str);
while (m.find())
System.out.println(str.substring(m.start(), m.end()));
}
}
/*----------------------------------------------------------------------------------------------------------------------
Karakter sabitlerinin \u ile yazılmasının regex için kullanımı
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String str = "1Ö223Ö";
String pattern = "^[a-z\u00D60-9]+$";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(str);
while (m.find())
System.out.println(str.substring(m.start(), m.end()));
}
}
/*----------------------------------------------------------------------------------------------------------------------
Düzenli ifadeler
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String str = "ch:(12):ch:(23) ali ch:(34) ";
String pattern = "ch:\\([0-9]+\\)";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(str);
while (m.find())
System.out.println(str.substring(m.start(), m.end()));
}
}
/*----------------------------------------------------------------------------------------------------------------------
Düzenli ifadeler
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class App {
public static void main(String[] args)
{
String str = "Bugün" + (char)13 + "hava\r" + (char)13 + "çok\r";
String [] words = str.split(String.format("[%c]+", (char)(13)));
for (String s : words)
System.out.println(s);
}
}
Gruplama işlemi parantezlerle yapılmakatadır ;
örneğin; ([a-z]3) bu kalıpta a ile z arasındaki karakterlerden, karakterinden 3 tane olabilir. xy_z gibi bir yazı geçerli olacaktır.
Sınıf çalışması
String sınıfınnın Split methodu RegEx kullanmadan ayraçları, bir String içierisine alacak bir method tasarlayınnız ve yazınız.
/*----------------------------------------------------------------------------------------------------------------------
Sınıf Çalışması: String sınıfının split metoduna kolay bir kullanım örneği
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
public class App {
public static void main(String[] args)
{
String str = "Bugün hava :;?çok güzel";
for (String s : Utils.split(str, " :\t;?", StringSplitOptions.REMOVEEMPTYENTRIES))
System.out.println(s);
}
}
enum StringSplitOptions {NONE, REMOVEEMPTYENTRIES}
class Utils {
public static String [] split(String s, String delims, StringSplitOptions op)
{
if (s == null || delims == null || delims.isEmpty() || op == null)
throw new IllegalArgumentException("Invalid arguments");
String regex = "[" + delims + "]";
if (op == StringSplitOptions.REMOVEEMPTYENTRIES)
regex += "+";
return s.split(regex);
}
}
Sınıf Çalışması :
Bir text dosya içerisinde satır satır kişi bilgileri bulunsun, kişilerin bilgileri şu formatta (Oğuz Karan Bahçeşehir 10/09/1976 Zonguldak ) fakat karışık şekilde olacaktır. Buna göre bu text dosyada bulunan kişilern yaşları ortalamasını bulan programı yazınız.
/*----------------------------------------------------------------------------------------------------------------------
Sınıf Çalışması: Bir text dosya içerisinde satır satır aşağıdaki gibi satırlarsdan
oluşsun
Oguz Karan Bahcesehir 10/09/1976 zonguldak
Burada ki bilgiler karışık sırada bulunabilir. Buna göre text dosya içerisindeki
bulunan kişilerin yaşları ortalamasını bulunuz.
Dosya bilgileri:
oguz karan bahcesehir 10/09/1976 zonguldak
zekiye cakiral 05/01/1991 trabzon uskudar
yusuf bicakcioglu kastamonu 15/10/1974 atasehir
oguzhan goller bakirkoy ardahan 10/01/1994
30/05/1983 gurur oner kirsehir bahcelievler
istanbul fatih serhat erbas 27/10/1988
ozgur sezgin kirklareli beyoglu 11/08/1982
mert demir bursa 06/09/1994 sisli
selman sahin giresun eyup 22/03/1984
ilker toksoz nevsehir bahcelievler 16/12/1986
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import org.csystem.utils.Util;
public class App {
public static void main(String[] args)
{
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("people.txt")))) {
String patternDate = "\\d{1,2}/\\d{1,2}/\\d{4}";
String patternDateParse = "[0-9]+";
String line;
LocalDate now = LocalDate.now();
double sum = 0;
int numberOfPeople = 0;
while ((line = br.readLine()) != null) {
++numberOfPeople;
String birthDateStr = Util.matchRegex(patternDate, line)[0];
String [] bdate = Util.matchRegex(patternDateParse, birthDateStr);
LocalDate birthDate = LocalDate.of(Integer.parseInt(bdate[2]), Integer.parseInt(bdate[1]), Integer.parseInt(bdate[0]));
sum += ChronoUnit.DAYS.between(birthDate, now) / 365.;
}
System.out.printf("Yaş ortalaması=%.2f%n", sum / numberOfPeople);
}
catch (Throwable ex) {
System.out.printf("%s:%s", ex.getClass().getName(), ex.getMessage());
}
}
}
Sınıf Çalışması:
Yukarıdaki sınıf çalışmasında kişilerin işe giriş tarihleri de herhangi bir yerde bulunsun(Satır içerisinde ) buna göre yaş ortalamasını bulan programı tekrar düzenleyiniz.
/*----------------------------------------------------------------------------------------------------------------------
Sınıf Çalışması: Yukarıdaki sınıf çalışmasında kişilerin işe giriş tarihleri de
rherhangi bir yerde (satır içerisinde) bulunsun buna göre yaş ortalamasını bulan
programı tekrar düzenleyiniz:
Dosya Bİlgileri:
oguz karan bahcesehir 01/10/2012 10/09/1976 zonguldak
zekiye cakiral 05/01/1991 trabzon 06/04/2015 uskudar
yusuf bicakcioglu kastamonu 15/10/1974 05/4/2016 atasehir
oguzhan goller bakirkoy ardahan 10/01/1994 07/10/2015
30/05/1983 gurur 15/7/2016 oner kirsehir bahcelievler
istanbul 10/08/2014 fatih serhat erbas 27/10/1988
ozgur sezgin 16/04/2014 kirklareli beyoglu 11/08/1982
mert demir bursa 01/01/2017 06/09/1994 sisli
selman sahin 05/11/2013 giresun eyup 22/03/1984
ilker toksoz nevsehir bahcelievler 16/12/1986 06/03/2016
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import org.csystem.utils.Util;
public class App {
public static LocalDate makeLocalDate(String [] birthDate)
{
return LocalDate.of(Integer.parseInt(birthDate[2]), Integer.parseInt(birthDate[1]), Integer.parseInt(birthDate[0]));
}
public static LocalDate minDate(LocalDate [] dates)
{
LocalDate result = dates[0];
for (int i = 1; i < dates.length; ++i)
if (result.isAfter(dates[i]))
result = dates[i];
return result;
}
public static void main(String[] args)
{
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("people.txt")))) {
String patternDate = "\\d{1,2}/\\d{1,2}/\\d{4}";
String patternDateParse = "[0-9]+";
String line;
LocalDate now = LocalDate.now();
double sum = 0;
int numberOfPeople = 0;
while ((line = br.readLine()) != null) {
++numberOfPeople;
String [] birthDateStr = Util.matchRegex(patternDate, line);
LocalDate [] dates = new LocalDate[birthDateStr.length];
int index = 0;
for (String s : birthDateStr) {
String [] bdate = Util.matchRegex(patternDateParse, s);
dates[index++] = makeLocalDate(bdate);
}
LocalDate birthDate = minDate(dates);
sum += ChronoUnit.DAYS.between(birthDate, now) / 365.;
}
System.out.printf("Yaş ortalaması=%.2f%n", sum / numberOfPeople);
}
catch (Throwable ex) {
System.out.printf("%s:%s", ex.getClass().getName(), ex.getMessage());
}
}
}