Как стать автором
Обновить

Комментарии 11

Не так просто пересадить клиента с xls на xlsx.
Обычно клиенты хотят поддержки обоих форматов, и чаще старый, нежели новый.
В данной ситуации повезло =)
Формула пишется в теге f (на одном уровне с v).

Тип (t) может быть «inlineStr», тогда строка записана не в SharedString, а прямо внутри ячейки, но в теге is (а не v) (хотя практически я с этим не сталкивался).

Целые числа и даты, а также вещественные числа и время (дата со временем) выглядят одинаково в теге v. Потому что дата, например, — это число дней с 01.01.1900.
Чтобы понять что это на самом деле нужно смотреть в атрибут s — это индекс стиля из workbook.xml.
P.S. Только расставляя метки наткнулся на Работа с большими файлами экселя. Хабрить надо было, а не гуглить — много бы времени сэкономил.

Там же через OLEDB работают.
Важно не само решение, а то, что практически в самом начале поста автор описывает (вракце) формат.
в отличие от бинарных xls, xlsx — по сути zip архив с данными.
НЛО прилетело и опубликовало эту надпись здесь
помню, что когда-то нужно было обработать несколько десятков msword документов из табличними данними — и вот почесалась репа и накидалось на коленке решение с использованием php streams:

$questions = array();
foreach(new DirectoryIterator(realpath('data')) as $file)
{
	if($file->isFile())
	{
		$path = "zip://" . $file->getPathname() . '#word/document.xml';
		_parse($path, $questions);
	}
}

function _parse($path, &$questions)
{
	$xml = simplexml_load_file($path);
	$ns = $xml->getDocNamespaces();
	$xml->registerXPathNamespace('w', $ns['w']);
	
	foreach($xml->xpath('/w:document/w:body/w:tbl') as $elem)
	{
А как экранируются служебные символы, например < >?
Методом проб и ошибок выяснил, что с помощью htmlspecialchars
P.s. Сам спросил, сам ответил. Надеюсь кому то будет полезно.
Дополнение от reak002

Хочу предложить небольшое дополнение к коду в виде:

$keyN = preg_replace('/\d/', '', $attr['r']);
$out[$file][$row][$keyN] = isset($attr['t']) ? $sharedStringsArr[$value] : $value;

вместо

$out[$file][$row][$cell] = isset($attr['t']) ? $sharedStringsArr[$value] : $value;
$cell++;

после чего код будет собирать полную сетку документа.

Вот собственно видоизмененный код:

$handle = @opendir(TEMP_DIR_PATH . '/' . $name.'/xl/worksheets');
$out = array();
while ($file = @readdir($handle)) {
    //проходим по всем файлам из директории /xl/worksheets/
    if ($file != "." && $file != ".." && $file != '_rels') {
        $xml = simplexml_load_file(TEMP_DIR_PATH . '/' . $name.'/xl/worksheets/' . $file);
        //по каждой строке
        $row = 0;
        foreach ($xml->sheetData->row as $item) {
            $out[$file][$row] = array();
            //по каждой ячейке строки
            foreach ($item as $child) {
                $attr = $child->attributes();
                $value = isset($child->v)? (string)$child->v:false;
                $keyN = preg_replace('/\d/', '', $attr['r']);
                $out[$file][$row][$keyN] = isset($attr['t']) ? $sharedStringsArr[$value] : $value;
            }
            $row++;
        }
    }
}
return $out;
Встретился с такой необходимостью, так как стали приходить битые xlsx файлы, которые надо было записывать в базу данных. С источником файлов было бесполезно договариваться.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории