Uzyskiwanie pierwszego znaku ciągu za pomocą $ str [0]

276

Chcę uzyskać pierwszą literę łańcucha i zauważyłem, że to $str[0]działa świetnie. Po prostu nie jestem pewien, czy jest to „dobra praktyka”, ponieważ notacja ta jest zwykle używana w przypadku tablic. Ta funkcja nie wydaje się być zbyt dobrze udokumentowana, więc zwracam się do was z prośbą, aby powiedzieć, czy w ogóle - pod każdym względem - użyć tej notacji?

A może powinienem po prostu trzymać się dobrej starej substr($str, 0, 1)?

Zauważyłem również, że nawiasy klamrowe ( $str{0}) również działają. O co chodzi?

Tatu Ulmanen
źródło
5
plus 1 za „dobry ol 'substr ($ str, 0, 1)”.
Santiago kończy pracę SO

Odpowiedzi:

390

Tak. Ciągi mogą być postrzegane jako tablice znaków, a sposobem dostępu do pozycji tablicy jest użycie []operatora. Zwykle nie ma żadnego problemu z używaniem $str[0](i jestem pewien, że jest znacznie szybszy niż substr()metoda).

Istnieje tylko jedno zastrzeżenie dotyczące obu metod: otrzymają one pierwszy bajt , a nie pierwszy znak . Jest to ważne, jeśli używasz kodowania wielobajtowego (takiego jak UTF-8). Jeśli chcesz to wesprzeć, użyj mb_substr(). Prawdopodobnie w dzisiejszych czasach należy zawsze zakładać wprowadzanie danych wielobajtowych, więc jest to najlepsza opcja, ale będzie ona nieco wolniejsza.

Pęcina
źródło
7
Czy PHP $ str [0] bierze pod uwagę, że mogą być 2-bajtowe znaki? UTF i takie? (chociaż substr () też nie pomaga!)
Tomer W
77
Jeśli chcesz być wyjątkowo bezpieczny, powinieneś iść z mb_substr($str, 0, 1, 'utf-8')tym, aby nie obcinać ciągu wielobajtowego.
Vic
18
Chociaż jest to krótsze i łatwiejsze do zapamiętania niż substr($str, 0, 1), to myli, kto czyta kod.
trante
10
Wybór między nawiasami kwadratowymi a substr () jest w dużej mierze kwestią preferencji, ale należy pamiętać, że wynik jest inny, gdy zastosuje się go do pustego łańcucha. Jeśli $ s = "", to $ s [] === "", ale substr ($ s, 0, 1) === false.
xtempore,
9
Jeśli $ s = "", to $ s [0] wygeneruje komunikat "Uwaga: Niezainicjowane przesunięcie łańcucha: 0", podczas gdy substr ($ s, 0, 1) nie.
Chris
46

Składnia {} jest przestarzała od PHP 5.3.0. Zalecane są nawiasy kwadratowe.

Michael Morton
źródło
14
docs.php.net/language.types.string :Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose. However, this syntax is deprecated as of PHP 5.3.0. Use square brackets instead, such as $str[42].
VolkerK
4
@VolkerK: pod podanym linkiem zauważyłem, że usunęli notatkę tylko z instrukcji PHP, którą zostawili: Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose.Zastanawiam się więc, czy zdecydowali, że używanie {}NIE jest już przestarzałe od PHP 6
Marco Demaio
1
@MarcoDemaio Link mówi teraz, co mówi MichaelMorton.
Tino
1
„nie podaje żadnych informacji o wycofaniu” - Rzeczywiście, komunikat o wycofaniu został usunięty w wersji 304518 - The curly-brackets-string-index-accessor-syntax does not emit any deprecation notice, although the original notice have been on and off for PHP 5.x, it does not in the current version, thrus we should not label it as deprecated. Related to bug #52254- svn.php.net/repository/phpdoc/en/trunk/language/types/…
VolkerK
Na dzień dzisiejszy (10 maja 18) cytat z polubionych dokumentów PHP : Note: Strings may also be accessed using braces, as in $str{42}, for the same purpose. Wygląda na to, że ta składnia pozostanie na jakiś czas.
Fr0zenFyr
25

Powiedzmy, że chcesz tylko pierwszego znaku z części $ _POST, nazwijmy go „typ”. A ten $ _POST [„typ”] jest obecnie „Kontrolą”. Jeśli w tym przypadku, jeśli używasz $_POST['type'][0], albo substr($_POST['type'], 0, 1)dostaniesz Cz powrotem.

Jednakże, jeśli po stronie klienta były zmodyfikować dane wysyłają od typedo type[]na przykład, a następnie wysłać „kontrola” i „test” jako danych dla tej tablicy, $_POST['type'][0]będzie teraz powrócić Controlzamiast Cnatomiast substr($_POST['type'], 0, 1)po prostu po prostu zawodzą.

Tak, może być problem z używaniem $str[0], ale zależy to od otaczających okoliczności.

gattsbr
źródło
2
Na marginesie, aby obejść ten konkretny problem, w obu przypadkach należy zawsze przeprowadzić weryfikację danych. if (true === is_string($_POST['type']))
fyrye
13

Moje jedyne wątpliwości dotyczyłyby zastosowania tej techniki do łańcuchów wielobajtowych, ale jeśli nie jest to rozważane, podejrzewam, że jesteś objęty. (W razie wątpliwości mb_substr()wydaje się to oczywiście bezpieczny wybór.)

Jednak z ogólnej perspektywy muszę zastanawiać się, jak często trzeba uzyskiwać dostęp do n-tego znaku w ciągu, aby było to kluczowym czynnikiem.

John Parker
źródło
9

Będzie się różnić w zależności od zasobów, ale możesz uruchomić skrypt poniżej i przekonaj się sam;)

<?php
$tests = 100000;

for ($i = 0; $i < $tests; $i++)
{
    $string = md5(rand());
    $position = rand(0, 31);

    $start1 = microtime(true);
    $char1 = $string[$position];
    $end1 = microtime(true);
    $time1[$i] = $end1 - $start1;

    $start2 = microtime(true);
    $char2 = substr($string, $position, 1);
    $end2 = microtime(true);
    $time2[$i] = $end2 - $start2;

    $start3 = microtime(true);
    $char3 = $string{$position};
    $end3 = microtime(true);
    $time3[$i] = $end3 - $start3;
}

$avg1 = array_sum($time1) / $tests;
echo 'the average float microtime using "array[]" is '. $avg1 . PHP_EOL;

$avg2 = array_sum($time2) / $tests;
echo 'the average float microtime using "substr()" is '. $avg2 . PHP_EOL;

$avg3 = array_sum($time3) / $tests;
echo 'the average float microtime using "array{}" is '. $avg3 . PHP_EOL;
?>

Niektóre numery referencyjne (na starej maszynie CoreDuo)

$ php 1.php 
the average float microtime using "array[]" is 1.914701461792E-6
the average float microtime using "substr()" is 2.2536706924438E-6
the average float microtime using "array{}" is 1.821768283844E-6

$ php 1.php 
the average float microtime using "array[]" is 1.7251944541931E-6
the average float microtime using "substr()" is 2.0931363105774E-6
the average float microtime using "array{}" is 1.7225742340088E-6

$ php 1.php 
the average float microtime using "array[]" is 1.7293763160706E-6
the average float microtime using "substr()" is 2.1037721633911E-6
the average float microtime using "array{}" is 1.7249774932861E-6

Wydaje się, że korzystanie z operatorów []lub {}jest mniej więcej takie samo.

Willy Stadnick
źródło
2
Niezły test! Niektóre liczby z 3-letniego Xeona: średni mikroprzełącznik zmiennoprzecinkowy przy użyciu „tablicy []” wynosi 2.2427082061768E-7 średni mikroprocesor zmiennoprzecinkowy przy użyciu „substr ()” wynosi 3,9647579193115E-7 średni mikroprocesor zmiennoprzecinkowy przy użyciu „tablicy {}” wynosi 2.1522283554077E-7
Ellert van Koperen
aby uzyskać dokładne pomiary, powinieneś lepiej wykonać mikroprzetwarzanie poza pętlą i nie mieszać różnych podejść w tej samej pętli.
PypeBros,
1
nie mieszanie wykonanie testAi testBw obrębie tej samej pętli oznacza, że jesteś w stanie wykrywać na przykład fakt, że testBjest cache-zabójca, gdy testAjest cache w obsłudze. Kiedy oba są w tej samej pętli, mierzone są tak, aby miały te same czasy, ponieważ buforowane przez testBzanieczyszczone testA.
PypeBros,
1
podobnie, unikałbym generowania ciągów lub losowości w pętlach testowych i przygotowałbym je w tablicy w pobliżu.
PypeBros,
1
-1; odkładając na bok wątpliwy mechanizm pomiaru czasu (lepiej byłoby zsynchronizować wiele operacji niż zliczać je pojedynczo; martwiłem się po przeczytaniu tego, że tylko czas poświęcony na wykonanie microtime()połączenia stanowiłby większość różnicy czasu, choć wydaje się to eksperymentalnie nie jest prawdą), nie ma powodu, aby przejmować się drobną różnicą prędkości tutaj. To ułamek milionowej sekundy; gdy jest to zawsze będzie ważne?
Mark Amery
6

Mówiąc jak zwykły śmiertelnik, trzymałbym się $str[0]. Jeśli o mnie chodzi, szybsze jest zrozumienie znaczenia $str[0]na pierwszy rzut oka niż substr($str, 0, 1). Prawdopodobnie sprowadza się to do preferencji.

Jeśli chodzi o wydajność, to profil profilu profil. :) Lub możesz zajrzeć do kodu źródłowego PHP ...

Stephen
źródło
6
$str = 'abcdef';
echo $str[0];                 // a
Jakir Hossain
źródło
6
-1; pytanie OP brzmiało, czy ta składnia była złą praktyką, a odpowiedziałeś ... powtarzając składnię, bez komentarza? To nie jest odpowiedź.
Mark Amery
5

W przypadku łańcuchów wielobajtowych (Unicode) używanie str[0]może powodować problemy. mb_substr()jest lepszym rozwiązaniem. Na przykład:

$first_char = mb_substr($title, 0, 1);

Kilka szczegółów tutaj: Uzyskaj pierwszy znak ciągu UTF-8

Sergey Burish
źródło
Dziękuję za to rozwiązanie! jeśli pierwszą postacią jest Unicode, [] nie będzie działać
SunB
1

Używałem tego zapisu również wcześniej, bez żadnych niepożądanych skutków ubocznych i nieporozumień. Ma to sens - w końcu ciąg znaków to po prostu tablica znaków.

Kaleb Brasee
źródło
Nie, ciąg nie jest tablicą znaków (przynajmniej, ponieważ PHP używa tych dwóch terminów). -1.
Mark Amery
@gattsbr wewnętrznie są, ale jeśli chodzi o model, który ujawnia PHP, są one zasadniczo inną rzeczą. Dostęp do przesunięcia za pomocą notacji w nawiasach kwadratowych jest właściwie jedyną operacją, którą mają one wspólne z tablicami; funkcje łańcuchowe nie działają na tablicach i odwrotnie, a składnia tablicowa append ( $arr[] = $new_element) nie działa na łańcuchach. W związku z tym nie sądzę, aby tworzenie łańcuchów znaków jako tablic znaków było przydatne.
Mark Amery
@markamery lepiej zacznij od przepisywania podręcznika php.net niż dołączenia takiej drobnej techniki.
gattsbr