İçeriğe geç
academia.sh

Ders 10 / 14

Dizgi İşleme

sprintf ve printf ile biçimlendirme; strlen ile mb_strlen arasındaki bayt ve karakter ayrımı; bozuk JSON'un json_decode'dan null olarak dönmesi; json_last_error ve JSON_THROW_ON_ERROR ile ayrımın yapılması; implode ve strlen'in dizi argümanındaki davranışı.

Yayın Güncelleme

Bu derste

  • sprintf ile sayı ve dizgi biçimlendirir ve genişlik belirtecinin baytla çalıştığını ölçer
  • Bozuk JSON'un json_decode'dan null döndürdüğünü ölçer ve dönüş sınıfına yazar
  • json_last_error ve JSON_THROW_ON_ERROR ile geçerli null'u bozuk girdiden ayırır
İçindekiler

Dizi Fonksiyonları dersi, dönüş kanalının ayrım gücünün çağıranın ayrım gücüyle sınırlı olduğunu gösterdi: 0 ile false ayırt edilmeyince bulunan ilk eleman “yok” sayıldı. Dizgi işlemede aynı sınır daha sert bir biçimde çıkıyor. Bir dizgi çözüldüğünde dönen null, hem geçerli bir null değerinden hem bozuk girdiden gelebiliyor ve değerin kendisi hangisinin olduğunu söylemiyor.

Dizgi (string) PHP’de bayt dizisidir; bir karakterin kaç bayt tuttuğu dizgiyi okuyan fonksiyona bağlıdır. Ders iki konu işliyor: dizgiyi biçimlendiren ve ölçen fonksiyonlar, ve dizgiyi JSON olarak kodlayıp çözen fonksiyonlar. Sınıf adları önceki derslerdeki anlamıyla kullanılır: sessiz, dönüş, uyarı, durdurur.

Biçimlendirme ve Bayt Ölçümü

sprintf bir biçim dizgisine göre değerleri yerleştirir; printf aynı biçimlendirmeyi doğrudan yazdırır. Genişlik, hizalama ve ondalık hane sayısı belirteçle verilir.

<?php
// bicim.php — sprintf ile bicimlendirme
echo sprintf('%05d', 42), "\n";
echo sprintf('%.2f', 3.14159), "\n";
echo sprintf('%-8s|', 'php'), "\n";
echo sprintf('%8s|', 'php'), "\n";
echo sprintf('%s kitap, %d sayfa', 'Ders', 120), "\n";
printf("%x %o %b\n", 255, 8, 5);
echo sprintf('%1$s-%2$s-%1$s', 'a', 'b'), "\n";
00042
3.14
php     |
     php|
Ders kitap, 120 sayfa
ff 10 101
a-b-a

Genişlik belirteci karakter değil bayt sayar. Çok baytlı bir harf içeren dizgide bu fark görünür; aşağıdaki dosya yalnız uzunlukları basıyor, hizalı bir sütun kurmuyor.

<?php
// bayt.php — strlen bayt, mb_strlen karakter sayar
$ad = 'señor';

echo 'strlen: ', strlen($ad), "\n";
echo 'mb_strlen: ', mb_strlen($ad), "\n";

$dolgulu = sprintf('%-8s', $ad);
echo 'dolgulu bayt: ', strlen($dolgulu), "\n";
echo 'dolgulu karakter: ', mb_strlen($dolgulu), "\n";

echo 'ascii strlen: ', strlen('senor'), "\n";
echo 'ascii mb_strlen: ', mb_strlen('senor'), "\n";
strlen: 6
mb_strlen: 5
dolgulu bayt: 8
dolgulu karakter: 7
ascii strlen: 5
ascii mb_strlen: 5

strlen('señor') altı, mb_strlen('señor') beş verdi: ñ iki bayttır ve tek karakterdir. sprintf('%-8s') ile sekiz bayta doldurulan dizgi yalnız yedi karakter genişliğinde görünür; hizalı bir sütunda bu kayma bir sonraki sütunu sola çeker. Tamamen ASCII olan senor için iki fonksiyon aynı sayıyı verir, bu yüzden fark yalnız çok baytlı veri geldiğinde ortaya çıkar. Sorun hiçbir kanaldan haber vermez: sessiz. Çok baytlı harf barındıran veri hizalı bir sütuna konmadan önce genişlik bayt değil karakter üzerinden hesaplanmalıdır.

DZ13. Bu ders mb_ ailesinin yalnız mb_strlen’ini kullanır ve girdinin tek bir kodlamada (UTF-8) olduğunu varsayar. Kodlamanın belirtilmediği ya da karışık olduğu girdi bu dersin dışındadır ve ölçülmez.

Bozuk JSON

JSON, dizileri ve ilişkisel dizileri metne çeviren bir değiş tokuş biçimidir. json_encode PHP değerini, json_decode metni PHP değerine çevirir. İkisi de başarısız olduğunda ne yaptığını dinleyici sınıflıyor; kanal.php aşağıda tam metniyle veriliyor ve çıktı bloğu olmadığı için yalnız diske yazılır.

<?php
// kanal.php — bir sorunun hangi kanaldan duyuldugunu siniflandiran yardimcilar

// Yakalanan uyarilarin seviye adlari burada birikir; ileti metni tutulmaz.
$GLOBALS['duyulan_uyarilar'] = [];

set_error_handler(function (int $seviye): bool {
    $adlar = [E_WARNING => 'E_WARNING', E_NOTICE => 'E_NOTICE', E_DEPRECATED => 'E_DEPRECATED'];
    $GLOBALS['duyulan_uyarilar'][] = $adlar[$seviye] ?? 'E_' . $seviye;
    return true;
});

// Degeri tipiyle birlikte kisa bir metne cevirir.
function yaz(mixed $deger): string
{
    return match (true) {
        $deger === null => 'null',
        is_bool($deger) => $deger ? 'true' : 'false',
        is_array($deger) => 'dizi(' . count($deger) . ')',
        default => get_debug_type($deger) . ' ' . $deger,
    };
}

// Isi calistirir ve sorunun hangi kanaldan duyuldugunu sirayla sorar:
// once firlatma, sonra uyari, sonra false/null donus; hicbiri yoksa sessiz.
function dinle(callable $is): array
{
    $GLOBALS['duyulan_uyarilar'] = [];
    try {
        $sonuc = $is();
    } catch (Throwable $hata) {
        return ['durdurur', get_class($hata)];
    }
    if ($GLOBALS['duyulan_uyarilar']) {
        $seviyeler = implode(',', array_unique($GLOBALS['duyulan_uyarilar']));
        return ['uyari', $seviyeler . ', sonuc ' . yaz($sonuc)];
    }
    if ($sonuc === false || $sonuc === null) {
        return ['donus', 'sonuc ' . yaz($sonuc)];
    }
    return ['sessiz', 'sonuc ' . yaz($sonuc)];
}

// Kodu ayri bir PHP yurutmesinde calistirir; cikis kodu 255 ise yurutme durmustur.
function ayri_yurut(string $kod): array
{
    exec('php -d display_errors=0 -r ' . escapeshellarg($kod) . ' 2>&1', $satirlar, $cikis);
    $sinif = $cikis === 255 ? 'durdurur' : 'sessiz';
    return [$sinif, 'cikti ' . count($satirlar) . ' satir, cikis ' . $cikis];
}
<?php
// json.php — json ve dizgi islemlerinde sorun kanallari
require 'kanal.php';

$gozlemler = [
    'bozuk json'        => fn() => json_decode('{bozuk'),
    'gecerli json'      => fn() => json_decode('{"a":1}', true),
    'bozuk kodlama'     => fn() => json_encode("\xB1"),
    'istisnali kodlama' => fn() => json_encode("\xB1", JSON_THROW_ON_ERROR),
    'ic ice implode'    => fn() => implode(',', [1, [2, 3]]),
    'dizi ile strlen'   => fn() => strlen([1, 2]),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-19s%-9s%s\n", $ad, $sinif, $kanit);
}
bozuk json         donus    sonuc null
gecerli json       sessiz   sonuc dizi(1)
bozuk kodlama      donus    sonuc false
istisnali kodlama  durdurur JsonException
ic ice implode     uyari    E_WARNING, sonuc string 1,Array
dizi ile strlen    durdurur TypeError

İlk satır kursun bozuk json gözlemidir: json_decode('{bozuk') null döndürdü, hiçbir seviye bildirilmedi. Sınıf dönüş. Üçüncü satır aynı kanalın kodlama yönü: geçersiz bir bayt dizisini kodlamak false döndürür. İkisi de bir hata olduğunu yalnız dönüş değeriyle söyler.

Dördüncü satır kanalın değiştiğini gösteriyor. JSON_THROW_ON_ERROR bayrağı verildiğinde aynı sorun JsonException olarak fırlar; sınıf durdurur. Bayrak, dönüş kanalındaki bir sorunu istisna kanalına taşır ve çağırana kontrolü bırakır: yakalamazsa yürütme durur. Beşinci satır dizgi işlemenin üçüncü kanalı: iç içe dizi implode edildiğinde iç dizi 'Array' dizgisine çevrilir ve E_WARNING bildirilir, yani uyarı. Sonuncusu tip kanalıdır: strlen bir dizi argümanıyla çağrıldığında TypeError fırlatır ve durdurur.

Metin Arama ve Kesme

Dizgi içinde arama yapan strpos, aranan başlangıçta bulunduğunda 0, hiç bulunamadığında false döndürür. Dizi Fonksiyonları dersindeki array_search ile aynı kanal ve aynı tuzak: gevşek karşılaştırma ikisini ayırt etmez. PHP bu iş için dönüş değerine bakmayı gerektirmeyen str_contains fonksiyonunu sunar.

<?php
// arama.php — dizgi arama ve kesme islevleri
$metin = 'php dersi';

echo 'strpos basta: ', var_export(strpos($metin, 'php'), true), "\n";
echo 'strpos yok: ', var_export(strpos($metin, 'js'), true), "\n";
echo 'gevsek basta: ', (strpos($metin, 'php') == false ? 'bulunamadi' : 'bulundu'), "\n";
echo 'str_contains basta: ', var_export(str_contains($metin, 'php'), true), "\n";
echo 'substr: ', var_export(substr($metin, 4), true), "\n";
echo 'substr uzak: ', var_export(substr($metin, 20), true), "\n";
echo 'explode: ', json_encode(explode(' ', $metin)), "\n";
echo 'bos ayirici: ', json_encode(explode(',', '')), "\n";
echo 'trim: ', var_export(trim("  a b \n"), true), "\n";
echo 'str_replace: ', str_replace('php', 'go', $metin), "\n";
strpos basta: 0
strpos yok: false
gevsek basta: bulunamadi
str_contains basta: true
substr: 'dersi'
substr uzak: ''
explode: ["php","dersi"]
bos ayirici: [""]
trim: 'a b'
str_replace: go dersi

strpos başta bulunan aranan için 0 döndürdü ve gevşek karşılaştırma bunu bulunamadi saydı; str_contains aynı soruyu true ile doğru yanıtladı. substr başlangıç dizgi sınırının ötesindeyse boş dizgi döndürür, hata vermez. explode boş bir dizgiyi ayırdığında boş liste değil, tek boş dizgili bir liste verir: tek elemanlı [""]. Bu sonuç bir sınır koşuludur ve “veri yok” ile “boş veri var” ayrımını sessizce siler.

DZ14. str_contains, str_starts_with ve benzerleri sorunu kanal olarak çözmez; yalnız soruyu doğru tipe çevirir. Kullanım rehberi yazmak bu dersin işi değildir ve yukarıdaki tabloda yer almayan davranışlar için bir iddia kurulmaz.

Çok baytlı bir harfi bayt düzeyinde kesen fonksiyon, geçersiz bir dizgi üretir ve bu dizgi bir sonraki kodlama çağrısında sorun çıkarır. Büyük harfe çevirme de aynı bayt/karakter ayrımına bağlıdır.

<?php
// kes.php — bayt duzeyinde kesmenin ve buyutmenin sonucu
$ad = 'señor';

$bayt_kesik = substr($ad, 0, 3);
$karakter_kesik = mb_substr($ad, 0, 3);

echo 'bayt kesik uzunluk: ', strlen($bayt_kesik), "\n";
echo 'bayt kesik gecerli mi: ', var_export(mb_check_encoding($bayt_kesik, 'UTF-8'), true), "\n";
echo 'bayt kesik json: ', var_export(json_encode($bayt_kesik), true), "\n";
echo 'karakter kesik uzunluk: ', mb_strlen($karakter_kesik), "\n";
echo 'karakter kesik gecerli mi: ', var_export(mb_check_encoding($karakter_kesik, 'UTF-8'), true), "\n";
echo 'buyutme ayni mi: ', var_export(strtoupper($ad) === mb_strtoupper($ad), true), "\n";
bayt kesik uzunluk: 3
bayt kesik gecerli mi: false
bayt kesik json: false
karakter kesik uzunluk: 3
karakter kesik gecerli mi: true
buyutme ayni mi: false

substr($ad, 0, 3) üç bayt aldı ve ñ harfinin ilk baytını kesip bıraktı; sonuç geçerli bir UTF-8 dizgisi değil ve json_encode onu false döndürerek reddetti. mb_substr aynı kesimi karakter sınırında yaptı ve sonuç geçerli kaldı. strtoupper yalnız ASCII harflerini büyütür; mb_strtoupper çok baytlı harfi de dönüştürür, bu yüzden iki sonuç eşit çıkmadı. Kesme ve büyütme fonksiyonunun bayt ile karakter arasındaki seçimi sorunu hiçbir kanaldan duyurmaz: kesik dizgi ancak sonradan, kodlama aşamasında dönüş değeriyle fark edilir.

Biçim Belirteçlerinin Sınırı

sprintf biçim dizgisindeki belirteçlerle argümanları eşler; belirteç sayısı, tipi ve sözdizimi çağrının kanalını belirler.

<?php
// belirtec.php — sprintf belirteclerinin sinirlari
require 'kanal.php';

$gozlemler = [
    'genislik ve duyarlik' => fn() => sprintf('%05.1f|%-4s|', 3.14159, 'ab'),
    'dolgu karakteri'      => fn() => sprintf("%'*8s", 'php'),
    'isaretli sayi'        => fn() => sprintf('%+d', 5),
    'dizgi sayi olarak'    => fn() => sprintf('%d', 'abc'),
    'eksik arguman'        => fn() => sprintf('%s %s', 'a'),
    'bilinmeyen belirtec'  => fn() => sprintf('%y', 1),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-21s%-9s%s\n", $ad, $sinif, $kanit);
}
genislik ve duyarlik sessiz   sonuc string 003.1|ab  |
dolgu karakteri      sessiz   sonuc string *****php
isaretli sayi        sessiz   sonuc string +5
dizgi sayi olarak    sessiz   sonuc string 0
eksik arguman        durdurur ArgumentCountError
bilinmeyen belirtec  durdurur ValueError

İlk üç satır belirteçlerin normal çalışmasını gösteriyor ve sessizdir. Dördüncü satır asıl noktadır: %d belirteci sayısal olmayan bir dizgiyi 0’a çevirir ve hiçbir seviye bildirmez; yanlış tipte veri biçimlendirmede sessizce kaybolur. Argüman sayısı belirteç sayısından az olduğunda ArgumentCountError, tanımsız bir belirteç yazıldığında ValueError fırlar; ikisi de durdurur. Aynı fonksiyon, bir hata sınıfında sessiz, bir başkasında durdurucu davranıyor: kanal çağrının hatasının türüne bağlıdır.

Doldurma, Sayı Biçimi ve Karşılaştırma

Sayıların ve dizgilerin görüntülenmesi için kullanılan birkaç yardımcı fonksiyon, hizalama ve ayırıcı gibi ayrıntıları argümanlara taşır.

<?php
// yardimci.php — doldurma, sayi bicimi ve dizgi karsilastirma
require 'kanal.php';

echo number_format(1234567.891, 2, ',', '.'), "\n";
echo number_format(0.5), ' ', number_format(1.5), ' ', number_format(2.5), "\n";
echo str_pad('7', 3, '0', STR_PAD_LEFT), "|", str_pad('ab', 6, '-', STR_PAD_BOTH), "|\n";
echo ucwords('php dizgi isleme'), "\n";
echo strrev('abc'), ' ', str_word_count('bir iki uc'), "\n";
echo 'strcmp: ', strcmp('a', 'b'), ' ', strcmp('b', 'a'), ' ', strcmp('a', 'a'), "\n";
echo 'spaceship: ', 'a' <=> 'b', ' ', 'b' <=> 'a', ' ', 'a' <=> 'a', "\n";

$gozlemler = [
    'sayisal dizgi esit' => fn() => '10' == '1e1',
    'sayisal dizgi kati' => fn() => '10' === '1e1',
    'baslangic sayisal'  => fn() => '12abc' + 1,
    'tam sayisal degil'  => fn() => 'abc' + 1,
    'sayiya cevirme'     => fn() => (int) '12abc',
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-20s%-9s%s\n", $ad, $sinif, $kanit);
}
1.234.567,89
1 2 3
007|--ab--|
Php Dizgi Isleme
cba 3
strcmp: -1 1 0
spaceship: -1 1 0
sayisal dizgi esit  sessiz   sonuc true
sayisal dizgi kati  donus    sonuc false
baslangic sayisal   uyari    E_WARNING, sonuc int 13
tam sayisal degil   durdurur TypeError
sayiya cevirme      sessiz   sonuc int 12

number_format ondalıkta yuvarlar (0.5, 1.5, 2.5 için 1 2 3) ve binlik ile ondalık ayırıcısı argümanlarla belirlenir. str_pad sol, sağ ya da iki yandan doldurur. strcmp ve <=> sıralama için aynı üç değeri verir.

Karşılaştırmada asıl dikkat edilecek nokta sayısal dizgilerdir: '10' == '1e1' doğrudur, çünkü iki taraf da sayıya çevrilir; === ise yanlış verir. '12abc' + 1 13 üretir ama E_WARNING verir, 'abc' + 1 ise TypeError ile durur. (int) '12abc' sessizce 12 verir; aynı girdi için dönüştürme sessiz, aritmetik uyarılı, tam sayısal olmayan dizgide aritmetik durdurucudur.

Sınırlayıcı Ölçüm

Dönüş kanalı ayırt edemez. Geçerli null değerinin çözümü ile bozuk girdinin çözümü aynı değeri verir.

<?php
// ayirt.php — null geri donusu iki ayri durumdan gelebilir
$bozuk = json_decode('{bozuk');
echo 'bozuk: ', var_export($bozuk, true), ' hata ', json_last_error(), "\n";

$gecerli = json_decode('null');
echo 'gecerli null: ', var_export($gecerli, true), ' hata ', json_last_error(), "\n";

echo 'esit mi: ', var_export($bozuk === $gecerli, true), "\n";
echo 'hata adi: ', json_last_error() === JSON_ERROR_NONE ? 'yok' : 'var', "\n";

try {
    json_decode('{bozuk', false, 512, JSON_THROW_ON_ERROR);
    echo "bozuk: firlatmadi\n";
} catch (JsonException $e) {
    echo 'bozuk: ', get_class($e), ' kod ', $e->getCode(), "\n";
}

$sonuc = json_decode('null', false, 512, JSON_THROW_ON_ERROR);
echo 'gecerli null, bayrakli: ', var_export($sonuc, true), "\n";
bozuk: NULL hata 4
gecerli null: NULL hata 0
esit mi: true
hata adi: yok
bozuk: JsonException kod 4
gecerli null, bayrakli: NULL

İki çağrı da null döndürdü ve === ile eşit çıktılar. Ayrımı json_last_error() yapıyor: bozuk girdide dört (JSON_ERROR_SYNTAX), geçerli null’da sıfır. Bu fonksiyon son çağrının hata durumunu taşır; bir sonraki json_decode çağrısı onu sıfırlar, bu yüzden hemen ardından okunması gerekir. Daha sağlam yol JSON_THROW_ON_ERROR bayrağıdır: bozuk girdi JsonException fırlatır, geçerli null ise null olarak döner ve iki durum artık ayrı kanallardan gelir.

DZ15. json_last_error() genel bir hata durumunu tutar ve ders onu yalnız json_decode çağrısının hemen ardından okur. Başka bir fonksiyonun araya girdiği durumlar ölçülmez. Ders hiçbir ini değerine güvenmez; JSON_THROW_ON_ERROR bayrağı çağrıya açıkça verilir.

Özet

  • sprintf ve printf biçim dizgisine göre değer yerleştirir; genişlik belirteci karakter değil bayt sayar.
  • strlen('señor') altı, mb_strlen('señor') beş verir; çok baytlı harf barındıran veri hizalı sütuna konmadan önce genişlik karakterle hesaplanmalıdır.
  • json_decode('{bozuk') null döndürür; bu dersin ölçü sınıfı dönüştür (bozuk json). Geçersiz bayt dizisi için json_encode false döndürür.
  • JSON_THROW_ON_ERROR aynı sorunu JsonException olarak fırlatır; sınıf durdurur.
  • Geçerli null ile bozuk JSON ikisi de null döndürür; ayrımı json_last_error() (sıfır ya da dört) ya da JSON_THROW_ON_ERROR yapar.
  • İç içe dizinin implode’u 'Array' ve E_WARNING verir; dizi argümanıyla strlen TypeError fırlatır.

Sonraki Adım

Dizilerin ve dizgilerin sorunları dört kanaldan duyulabiliyor: sessiz, dönüş, uyarı ve durdurur. Sıradaki konu, verinin kodun içinden değil, dışarıdan geldiği yerdir. Süper Küresel Değişkenler dersi, istek verisi eksik olduğunda hangi kanaldan ne duyulduğunu ölçüyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat