За последние 24 часа нас посетили 16503 программиста и 1569 роботов. Сейчас ищут 933 программиста ...

Подарок от гугла (нулевой байт)

Тема в разделе "PHP для новичков", создана пользователем demoniqus, 5 сен 2012.

  1. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Из mail.google.com можно выгружать контакты как в формате CSV outlook'a, так и и в собственном формате CSV гугла. Первый вариант выгружается нормально, его легко можно распарсить. А вот с собственным CSV-форматом гугла вышла неувязочка. Мне нужно было выдрать определенные поля (их порядковые номера) - я решил распарсить первую строку и по наименованиям получить номера интересующих полей. Т.е. к примеру мне нужно поле Name, я делаю так
    Код (Text):
    1.  
    2. $data = fgetcsv($handle, 0, $delimiter);
    3. for ($i = 0; $i < count ($data); $i++) {
    4.   if ($data[$i] == 'Name') {
    5.   // some code ...
    6.   }
    7. }
    Вроде бы все легко и просто, а ни при каком $i равенство не выходит. Я и так и сяк крутил, а равенства нет. Тогда я прогнал $data[$i] через ord() - оказалось, что все видимые символы разделены символом с нулевым кодом, т.е. примерно так
    [код символа][0][код символа][0][код символа][0]
    Подскажите, пожалуйста, как решить данную проблему - избавиться от этого нулевого байта или привести искомую строку (в примере Name) к такому же виду. Есть и еще одна трудность: не везде у меня идет сравнение на строгое равенство, а кое-где используется поиск по регулярке...
     
  2. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    зачем тогда мучаться?
     
  3. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    ну вот нужно и тот и другой вариант учитывать...
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    explode("\x00", $str)
     
  5. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Не все так просто оказалось. Это Unicode - для латиницы второй байт пустой, а для кириллицы уже не пустой. Я попробовал перевести обычную строку в юникод - последовательность кодов совпала, но, если я делаю вот так
    Код (Text):
    1.  
    2. $line = 'Кириллица';
    3. echo 'line = ' . $line . '(';
    4. for($z=0;$z<strlen($line);$z++)
    5.   echo '[' . ord($line[$z]) . '] ';
    6. echo ')';
    то получаю такой результат
    line = ��8@8;;8F0([255], [254], [26], [4], [56], [4], [64], [4], [56], [4], [59], [4], [59], [4], [56], [4], [70], [4], [48], [4], )
    строку предваряют символ из двух байтов 255 и 254, который не является пробельным - trim его не удаляет
     
  6. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    Ну тогда почему бы тебе предварительно не преобразовать кодировку в удобную для тебя?
    iconv
     
  7. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    если бы все так было просто, я бы не задавал вопрос))))))
    При попытке перекодировать гугловские данные получается вообще фигня... А, перекодируя искомые данные, я сталкиваюсь с тем, что:
    1) в начало втыкается непонятный символ [255][254]
    2) пока не удалось грамотно перекодировать регулярное выражение, чтобы оно оставалось рабочим - постоянно вылезало сообщение no ending delimiter found, хотя вроде все спецсимволы я и пытался перекодировать и оставлял их в неизменном виде
    и вообще, как оказалось, нет ничего общего у выгрузки из гугла контактов методом Outlook с реальной выгрузкой контактов из Outlook'a. Контакты из outlook'a выгрузились в cp1251 и никаких проблем не вызвали при обработке.

    Тут возник еще прелюбопытный вопрос - якобы в php крайне не рекомендуется извлекать из строкового значения отдельные символы как элементы массива, т.е. не рекомендуется делать вот так:
    Код (Text):
    1.  
    2. $string = 'some string';
    3. $index = 0; //some number value
    4. echo $string[$index];
    Для этих целей рекомендуется либо использовать substr со смещением, либо обращаться не через квадратные, а через фигурные скобки:
    Код (Text):
    1.  
    2. $string = 'some string';
    3. $index = 0; //some number value
    4. echo $string{$index};
    Почему так?
     
  8. sobachnik

    sobachnik Старожил

    С нами с:
    20 апр 2007
    Сообщения:
    3.380
    Симпатии:
    13
    Адрес:
    Дмитров, МО
    Если использовать кодировку utf-8 и в строке текста будут русские символы - то такой подход
    Код (PHP):
    1. $string{$index} 
    просто не будет работать как надо. Русские символы в кодировке utf-8 занимают два байта. А вышеприведённое выражение в PHP - выбирает байты из строки. Не символы, а именно байты. То есть будет получено "пол-символа". :)
     
  9. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Мне на то, что не рекомендуется обращаться к строке, как к массиву, давали следующие аргументы:
    1) При попытке обратиться по индексу к несуществующему элементу (т.е. строка к примеру состоит из десяти символов, а обращаемся к двадцатому) вылетает ошибка
    2) Вопрос о том, насколько больше нагружает обращение по индексу по сравнению с поиском через {} или функцией substr
    провел маленький эксперимент:
    3) В книжке написано, что рекомендуется использовать имена переменных длиной до 7 символов, т.к. они существенно быстрее обрабатываются
    Код (Text):
    1.  
    2. $str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    3. echo '<span style="color: red">str[320] = ' . ($str[320]) . '</span><br /><br />';
    4. echo '<span style="color: red">str{320} = ' . ($str{320}) . '</span><br /><br />';
    5. $i = 0;
    6. $start = microtime(true);
    7. while ($i < 1000000) {
    8.     $s = substr($str, 150, 1);
    9.     $i++;
    10. }
    11. echo 'substr (str) = ' . (microtime(true) - $start) . '<br /><br />';
    12. $i = 0;
    13. $start = microtime(true);
    14. while ($i < 1000000) {
    15.     $s = $str[150];
    16.     $i++;
    17. }
    18. echo '[] (str) = ' . (microtime(true) - $start) . '<br /><br />';
    19. $i = 0;
    20. $start = microtime(true);
    21. while ($i < 1000000) {
    22.     $s = $str{150};
    23.     $i++;
    24. }
    25. echo '{} (str) = ' . (microtime(true) - $start) . '<br /><br />';
    26.  
    27.  
    28. $string7 = $str;
    29. $i = 0;
    30. $start = microtime(true);
    31. while ($i < 1000000) {
    32.     $s = substr($string7, 150, 1);
    33.     $i++;
    34. }
    35. echo 'substr ($string7) = ' . (microtime(true) - $start) . '<br /><br />';
    36. $i = 0;
    37. $start = microtime(true);
    38. while ($i < 1000000) {
    39.     $s = $string7[150];
    40.     $i++;
    41. }
    42. echo '[] ($string7) = ' . (microtime(true) - $start) . '<br /><br />';
    43. $i = 0;
    44. $start = microtime(true);
    45. while ($i < 1000000) {
    46.     $s = $string7{150};
    47.     $i++;
    48. }
    49. echo '{} ($string7) = ' . (microtime(true) - $start) . '<br /><br />';
    50.  
    51.  
    52. $werylengthstringname = $str;
    53. $i = 0;
    54. $start = microtime(true);
    55. while ($i < 1000000) {
    56.     $s = substr($werylengthstringname, 150, 1);
    57.     $i++;
    58. }
    59. echo 'substr ($werylengthstringname) = ' . (microtime(true) - $start) . '<br /><br />';
    60. $i = 0;
    61. $start = microtime(true);
    62. while ($i < 1000000) {
    63.     $s = $werylengthstringname[150];
    64.     $i++;
    65. }
    66. echo '[] ($werylengthstringname) = ' . (microtime(true) - $start) . '<br /><br />';
    67. $i = 0;
    68. $start = microtime(true);
    69. while ($i < 1000000) {
    70.     $s = $werylengthstringname{150};
    71.     $i++;
    72. }
    73. echo '{} ($werylengthstringname) = ' . (microtime(true) - $start) . '<br /><br />';
    Результаты (пока эксперимент проводился под windows 7 максималка):
    str[320] =
    str{320} =

    substr (str) = 0.5581591129303
    [] (str) = 0.28874492645264
    {} (str) = 0.2863290309906

    substr ($string7) = 0.55512809753418
    [] ($string7) = 0.29122090339661
    {} ($string7) = 0.28695893287659

    substr ($werylengthstringname) = 0.55270504951477
    [] ($werylengthstringname) = 0.28703498840332
    {} ($werylengthstringname) = 0.28970217704773

    В первой строке сразу видно, что никакой ошибки при обращении к несуществующему индексу не вылетает ни при [], ни при {}.
    Далее идут три блока: блок, в котором имя переменной состоит из трех символов, блок с переменной из 7 символов и блок с переменной из 20 символов. Как видно, разницы никакой нет. Также нет разницы по времени между обращением через [] и через []. Зато substr отрабатывает вдвое медленнее. Так что пока так и осталось непонятным, почему не рекомендуется обращаться со строкой, как с массивом при извлечении символов...
     
  10. sobachnik

    sobachnik Старожил

    С нами с:
    20 апр 2007
    Сообщения:
    3.380
    Симпатии:
    13
    Адрес:
    Дмитров, МО
    Для того, чтобы увидеть сообщение об ошибке - нужно, чтобы было включено их отображение и чтобы выводились все ошибки, в том числе и нотисы:
    Код (PHP):
    1. <?php
    2. ini_set('display_errors', true);
    3. $str = 'abcdefg';
    4. echo($str{9});
    5. ?>
    Этот код приведёт к ошибке
    А ты читал моё предыдущее сообщение?
    На счёт скорости выполнения - да, обращение по индексу работает быстрее. Но при использовании кодировки utf-8 (и вообще любой не однобайтовой кодировки) - на практике не применимо.
     
  11. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Про репортирование обо всех ошибках забыл...
    Остается открытым вопрос, насколько это критично и правильно: либо жертвовать скоростью (вряд ли когда-нибудь придется перебирать мегабайты текста посимвольно), либо забить на эти нотисы и писать более читабельно и кратко?
     
  12. sobachnik

    sobachnik Старожил

    С нами с:
    20 апр 2007
    Сообщения:
    3.380
    Симпатии:
    13
    Адрес:
    Дмитров, МО
    Я бы использовал $str{$index} - если программа не предполагает работу с текстом в кодировке utf-8 (то есть если использется однобайтная кодировка, типа Windows-1251). Если используется кодировка utf-8 - только функции mb_string.

    Добавлено спустя 2 минуты 17 секунд:
    И да, при использовании $str{$index} - я бы нотисов избегал, заранее определяя длину строки, а не обращаясь "вслепую" к какому-то символу в строке. Впрочем, при использовании функций - я тоже сперва определю длину строки.