Welcome to php club

PHP FAQ from PHPclub.ru: ProblemsFgetcvs ...

Начало | Каталог | Изменения | НовыеКомментарии | Вам запрещён доступПользователи | Вам запрещён доступРегистрация | Вход:  Пароль:  

Php Problems

Почему теряются русские буквы, если использовать функцию fgetcsv() под Windows, и что с этим делать?


Собственно, сабж. На сайте bugs.php.net ваш покорный слуга нашёл 2 багрепорта на данную тему:
http://bugs.php.net/bug.php?id=12127
http://bugs.php.net/bug.php?id=21689


Ознакомившись с вышеприведёнными материалами, вот тот ответ, который можно считать оптимальным решением проблемы:
This problem never occurs on the systems that use GNU libc and can also be reproduced with Sun's libc or Microsoft's libc AFAIK.
--moriyoshi /at\ php.net, 28 Feb 7:29am CST 


Иными словами, нужно собирать PHP из исходников, используя правильные библиотеки. Хотя, по идее, проблемы с библиотеками должны решать и сами разработчики PHP, собирая «правильные» бинарники.


Теперь о проявлениях проблемы. У себя дома за компьютером (Win98, Apache 1.3.27, PHP 4.2.1) я решил опробовать первый пример из первого багрепорта, и в первый раз получил следующий результат:


5 fields in line 1:
Любимое 5,58
5 fields in line 2:
Любимое (витаминизированное) 345 9,15


, что, собственно, и требовалось. Тем не менее, при очередном обновлении страницы некоторые буквы всё-таки периодически пропадали.


Yurik, который провёл то же самое тестирование на своей машине (XP Pro 2002 Eng SP1, Apache 1.3.27, PHP 4.3.2), получил такие данные:


5 fields in line 1:
Люби 5,58
5 fields in line 2:
Любимое (витаминизированное) 345 9,15


Как видим, некоторые буквы оказались также «проглоченными», несмотря на более новую версию PHP у Yurik. Одним словом, на эту функцию не стоит полагаться.


А надёжнее всего не использовать эту функцию PHP под Windows, а написать свою собственную (к тому же кто в таком случае будет виноват в ошибках? Вот-вот ). Вот функция, которую я написал для своих собственных нужд (правда, не на C, а на PHP, что несколько скажется на производительности в среде Windows, ибо в других ОС вызывается native API function

<?
fgetcsv_club
($f_handle, $length, $delimiter=',', $enclosure='"')
{
    if (!
strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
        return
fgetcsv($f_handle, $length, $delimiter, $enclosure);
    if (!
$f_handle || feof($f_handle))
        return
false;
           
    if (
strlen($delimiter) > 1)
        
$delimiter = substr($delimiter, 0, 1);
    elseif (!
strlen($delimiter))          // There _MUST_ be a delimiter
        
return false;
                       
    if (
strlen($enclosure) > 1)         // There _MAY_ be an enclosure
        
$enclosure = substr($enclosure, 0, 1);
                       
    
$line = fgets($f_handle, $length);
    if (!
$line)
        return
false;
    
$result = array();
    
$csv_fields = explode($delimiter, trim($line));
    
$csv_field_count = count($csv_fields);
    
$encl_len = strlen($enclosure);
    for (
$i=0; $i<$csv_field_count; $i++)
    {
        
// Removing possible enclosures
        
if ($encl_len && $csv_fields[$i]{0} == $enclosure)
            
$csv_fields[$i] = substr($csv_fields[$i], 1);
        if (
$encl_len && $csv_fields[$i]{strlen($csv_fields[$i])-1} == $enclosure)
            
$csv_fields[$i] = substr($csv_fields[$i], 0, strlen($csv_fields[$i])-1);
        
// Double enclosures are just original symbols
        
$csv_fields[$i] = str_replace($enclosure.$enclosure, $enclosure, $csv_fields[$i]);
        
$result[] = $csv_fields[$i];
    }
    return
$result;
}
?>


Функция имеет список аргументов, и возвращаемое значение, идентичные оригинальной функции, но она срабатывает всегда одинаково.


Принцип работы функции очень прост:
--
* Из файла считывается очередная строка;
* Строка разбивается на куски указанным символом разделения CSV полей;
* В каждом куске из начала и конца убираются символы-ограничители поля, дублированные символы являются не ограничителями, а обозначением оригинального символа (т.е., к примеру, при ограничителе '"', таким образом, текст '"abc""def"' будет заменён на 'abc"def');
* Функцией возвращается массив с полученными результатами.
--



 
Комментариев нет. [Показать комментарии/форму]