Формат файла .bin Office 2007 - Часть листа

ОГЛАВЛЕНИЕ

Часть листа

Часть листа описывает значения в ячейках, вместе с всегда применяемыми им формулами, а также описывает увязку объектов с ячейками или диапазонами ячеек (диаграммами, сводными таблицами, ...). Ниже приведен пример разобранного листа, содержащего число двойной точности с плавающей запятой, формулу с плавающей запятой, возвращающую ошибку "деление на нуль", и копию этой ячейки в другом месте:

 

Разбор структуры числовых значений ячеек с и без формул

// Так часть листа выглядит в XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet
    xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/5/main"
           xmlns:r=
           "http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <dimension ref="D3:D6"/>
  <sheetViews>
    <sheetView tabSelected="1" workbookViewId="0">
       <selection activeCell="D4" sqref="D4"/>
    </sheetView>
  </sheetViews>
  <sheetFormatPr defaultRowHeight="15"/>
  <cols>
    <col min="4" max="4" width="12.5703125" customWidth="1"/>
  </cols>
  <sheetData>
    <row r="3" spans="4:4"><c r="D3"><v>
        2.123456789</v></c></row>
    <row r="4" spans="4:4"><c r="D4" t="e"><f>5/E3</f><v>#DIV/0!</v>
    </c></row>
    <row r="6" spans="4:4"><c r="D6" t="e"><v>
    #DIV/0!</v></c></row>
  </sheetData>
  <printOptions/>
  <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75"
  header="0.3" footer="0.3"/>
  <headerFooter/>
</worksheet>


// Таким образом после разбиения части BIN на записи можно сопоставить
// записи с разметкой XML.

// Обратите внимание, что идентификаторы записей являются двумя байтами слева,
// связанная длина записи обрамлена круглыми скобками,
// а за ними идет само содержимое записи.

<worksheet/>
81 01 (00)

<sheetPr/> (figured out)
93 01 (0f) c9 04 02 00 40 00 00 00 00 00 00 00 00 00 00

<dimension ref="D3:D6"/>
94 01 (10) 02 00 00 00 05 00 00 00 03 00 00 00 03 00 00 00

<sheetViews>
85 01 (00)

<sheetView tabSelected="1" workbookViewId="0">
89 01 (1e) dc 03 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 64
00 00 00 00 00 00 00 00 00 00 00

<selection activeCell="D4" sqref="D4"/>
98 01 (24) 03 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 01 00 00
00 02 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00

</sheetView>
8a 01 (00)

</sheetViews>
86 01 (00)

<sheetFormatPr defaultRowHeight="15"/>
e5 03 (0c) ff ff ff ff 08 00 2c 01 00 00 00 00

<cols>
86 03 (00)

<col min="4" max="4" width="12.5703125" customWidth="1"/>
3c (12)
            03 00 00 00 colmin (0-based)
            03 00 00 00 colmax (0-based)
            92 0c 00 00 width * 256
            00 00 00 00 style  (0-based)
            02 00       flags

</cols>
87 03 (00)

<sheetData>
91 01 (00)

<row r="3" spans="4:4"></row>
00 (19) 02 00 00 00 00 00 00 00 2c 01 00 00 00 01 00 00 00 03 00 00
00 03 00 00 00

<c r="D3"><v>2.123456789</v></c>
05 (10)
            03 00 00 00             col   (0-based)
            00 00 00 00             style (0-based)
            1b cb b9 e9 d6 fc 00 40 float (IEEE 8 bytes)

<row r="4" spans="4:4"></row>
00 (19) 03 00 00 00 00 00 00 00 2c 01 00 00 00 01 00 00 00 03 00 00
00 03 00 00 00

<c r="D4" t="e"><f>5/E3</f><v>
#DIV/0!</v></c>
0b (1e)
            03 00 00 00              col   (0-based)
            00 00 00 00              style (0-based)
            07                       boolerr (7 = DIV/0)
            00 00                    grbits
            0b 00 00 00              len of formula to follow in bytes
            1e 05 00 44 02 00 00 00 04 c0 06 formula (1E = ptgTokenInt  (5)
            00 00 00 00                               44 = ptgTokenRefV (E3)
                                                      06 = ptgTokenDiv)

<row r="6" spans="4:4"></row>
00 (19) 05 00 00 00 00 00 00 00 2c 01 00 00 00 01 00 00 00 03 00 00
00 03 00 00 00

<c r="D6" t="e"><v>#DIV/0!</v></c>
03 (09)
            03 00 00 00              col
            00 00 00 00              style
            07                       boolerr (7 = DIV/0)

</sheetData>
92 01 (00)

97 04 (42) 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00

<conditionalFormatting sqref="B3:C4">
cd 03 (1c) 01 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00
00 00 01 00 00 00 02 00 00 00

<cfRule type="dataBar" priority="3">
cf 03 (8c) 02 04 00 00 00 03 00 00 00 ff ff ff ff 03 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 67 00 00 00 67 00 00 00 00
00 00 00 ff ff ff ff
    <formula>MAX(IF(ISBLANK($B$3:$C$4), "",
    IF(ISERROR($B$3:$C$4), "", $B$3:$C$4)))</formula>
    67 00 00 00 65 02 00 00 00 03 00 00 00 01 00 02 00 61 81 00 19
    02 0b 00 19 40 00 01 17 00 00 19 08 43 00 65 02 00 00 00 03 00
    00 00 01 00 02 00 61 03 00 19 02 0b 00 19 40 00 01 17 00 00 19
    08 1c 00 19 40 00 01 25 02 00 00 00 03 00 00 00 01 00 02 00 19
    40 00 01 19 08 03 00 22 03 01 00 19 08 03 00 22 03 01 00 42 01
    07 00 00 00 00 00
    <formula>MAX(IF(ISBLANK($B$3:$C$4), "", IF(ISERROR($B$3:$C$4),
    "", $B$3:$C$4)))</formula>
    67 00 00 00 65 02 00 00 00 03 00 00 00 01 00 02 00 61 81 00 19
    02 0b 00 19 40 00 01 17 00 00 19 08 43 00 65 02 00 00 00 03 00
    00 00 01 00 02 00 61 03 00 19 02 0b 00 19 40 00 01 17 00 00 19
    08 1c 00 19 40 00 01 25 02 00 00 00 03 00 00 00 01 00 02 00 19
    40 00 01 19 08 03 00 22 03 01 00 19 08 03 00 22 03 01 00 42 01
    06 00 00 00 00 00

<dataBar>
d3 03 (03) 0a 5a 01

<cfvo type="min" val="0" />
d7 03 (18) 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00

<cfvo type="max" val="0" />
d7 03 (18) 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00

<color rgb="FF638EC6" /> (hint : color encoding = BGR not RGB)
b4 04 (08) 05 ff 00 00 63 8e c6 ff

</dataBar>
d4 03 (00)

</cfRule>
d0 03 (00)

</conditionalFormatting>
ce 03 (00)


<conditionalFormatting sqref="B6:C7">
cd 03 (1c) 01 00 00 00 00 00 00 00 01 00 00 00 05 00 00 00 06 00 00
00 01 00 00 00 02 00 00 00

<cfRule type="colorScale" priority="2">
cf 03 (8c) 02 03 00 00 00 02 00 00 00 ff ff ff ff 02 00 00 00 00 00
00 00 00 00 00 00 00 00 00
00 00 00 67 00 00 00 67 00 00 00 00 00 00 00 ff ff ff ff
    <formula>MAX(IF(ISBLANK($B$6:$C$7), "", IF(ISERROR($B$6:$C$7),
    "", $B$6:$C$7)))</formula>
    67 00 00 00 65 05 00 00 00 06 00 00 00 01 00 02 00 61 81 00 19 02
    0b 00 19 40 00 01 17 00 00 19 08 43 00 65 05 00 00 00 06 00 00 00
    01 00 02 00 61 03 00 19 02 0b 00 19 40 00 01 17 00 00 19 08 1c 00
    19 40 00 01 25 05 00 00 00 06 00 00 00 01 00 02 00 19 40 00 01 19
    08 03 00 22 03 01 00 19 08 03 00 22 03 01 00 42 01 07 00 00 00 00 00
    <formula>MAX(IF(ISBLANK($B$6:$C$7), "", IF(ISERROR($B$6:$C$7),
    "", $B$6:$C$7)))</formula>
    67 00 00 00 65 05 00 00 00 06 00 00 00 01 00 02 00 61 81 00 19 02
    0b 00 19 40 00 01 17 00 00 19 08 43 00 65 05 00 00 00 06 00 00 00
    01 00 02 00 61 03 00 19 02 0b 00 19 40 00 01 17 00 00 19 08 1c 00
    19 40 00 01 25 05 00 00 00 06 00 00 00 01 00 02 00 19 40 00 01 19
    08 03 00 22 03 01 00 19 08 03 00 22 03 01 00 42 01 06 00 00 00 00 00

<colorScale>
d5 03 (00)

<cfvo type="min" val="0" />
d7 03 (18) 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00

<cfvo type="percent" val="50" />
d7 03 (18) 04 00 00 00 00 00 00 00 00 00 49 40 00 00 00 00 00 00 00 00
00 00 00 00

<cfvo type="max" val="0" />
d7 03 (18) 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00

<color rgb="FFF8696B" /> (hint : color encoding = BGR not RGB)
b4 04 (08) 05 ff 00 00 f8 69 6b ff

<color rgb="FFFFEB84" /> (hint : color encoding = BGR not RGB)
b4 04 (08) 05 ff 00 00 ff eb 84 ff

<color rgb="FF63BE7B" /> (hint : color encoding = BGR not RGB)
b4 04 (08) 05 ff 00 00 63 be 7b ff

</colorScale>
d6 03 (00)

</cfRule>
d0 03 (00)

</conditionalFormatting>
ce 03 (00)


<conditionalFormatting sqref="B9:C10">
cd 03 (1c) 01 00 00 00 00 00 00 00 01 00 00 00 08 00 00 00 09 00 00 00 01
00 00 00 02 00 00 00

<cfRule type="iconSet" priority="1">
cf 03 (2e) 06 00 00 00 04 00 00 00 ff ff ff ff 01 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff

<iconSet iconSet="3TrafficLights2">
d1 03 (06) 04 00 00 00 78 00

<cfvo type="percentile" val="0" />
d7 03 (18) 05 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00
00 00 00

<cfvo type="percentile" val="0" />
d7 03 (18) 05 00 00 00 00 00 00 00 00 80 40 40 01 00 00 00 01 00 00 00 00
00 00 00

<cfvo type="percentile" val="0" />
d7 03 (18) 05 00 00 00 00 00 00 00 00 c0 50 40 01 00 00 00 01 00 00 00 00
00 00 00

</iconSet>
d2 03 (00)

</cfRule>
d0 03 (00)

</conditionalFormatting>
ce 03 (00)

<hyperlink ref="C3" r:id="rId1"/>
ee 03 (28) 02 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00 04 00 00 00 72
00 49 00 64 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00

<printOptions/>
dd 03 (02) 10 00

<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75"
header="0.3" footer="0.3"/>
dc 03 (30) 66 66 66 66 66 66 e6 3f 66 66 66 66 66 66 e6 3f 00 00
00 00 00 00 e8 3f 00 00 00 00 00 00
e8 3f 33 33 33 33 33 33 d3 3f 33 33 33 33 33 33 d3 3f

<headerFooter/>
df 03 (1a) 0c 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff

e0 03 (00)

</worksheet>
82 01 (00)

Выше есть примеры нижележащей структуры, один содержит измененные столбцы, другой содержит строки и фактические ячейки. Каждая ячейка хранит конкретный тип значения, что отражает идентификатор записи. Он отличается для необработанного значения и для значения со связанной формулой. Следовательно,

//Записи ячейки
public const int BIFF12_ROW             = 0x00;
                    // информация строки
public const int BIFF12_BLANK           = 0x01;
                    // пустая ячейки
public const int BIFF12_NUM             = 0x02;
                    // число одинарной точности с плавающей запятой
public const int BIFF12_BOOLERR         = 0x03;
                    // идентификатор ошибки
public const int BIFF12_BOOL            = 0x04;
                    // логическое значение
public const int BIFF12_FLOAT           = 0x05;
                    // число двойной точности с плавающей запятой
public const int BIFF12_STRING          = 0x07;
                // строка (индекс общей строки)
public const int BIFF12_FORMULA_STRING  = 0x08;
                // формула, возвращающая строку (встроенная строка)
public const int BIFF12_FORMULA_FLOAT   = 0x09;
                // формула, возвращающая число двойной точности с плавающей запятой
public const int BIFF12_FORMULA_BOOL    = 0x0A;
                // формула, возвращающая логическое значение
public const int BIFF12_FORMULA_BOOLERR = 0x0B;
                // формула, возвращающая идентификатор ошибки

//Надо сделать: найти код операции для встроенных строк (вероятно, это 0x06).

Далее рассказано, как хранятся формулы:

Байткод формулы

Байткод формулы, генерируемый Excel при разборе формул, в основном задокументирован (сравните официальную документацию по всем ptgTokens). Для разбора или вычисления формул надо знать, что они хранятся в обратном порядке (обратная польская нотация), то есть 5+3 хранится как {5,3,+}. В остальных случаях можно использовать существующий простой парсер/вычислитель RPN, написанный на C++ и расположенный тут. Исходя из этого, Excel 2007 вводит следующие изменения:

•    Так как есть гораздо больше строк и столбцов на каждый лист, байткоду формулы приходится находить ноые сегменты для хранения строк в 4 байтах вместо 2 (раньше максимальное число строк = 65536) и хранить столбцы в 2 байтах вместо 1 (раньше максимальное число столбцов = 256). Поэтому, по сравнению с общим кодированием, любая строка теперь является 4-байтным сегментом. Для столбцов сегмент не меняется, потому что на самом деле любая ссылка на столбец ячейки уже хранилась в двух байтах вместо одного. При этом, два самых старших битов двух байтов всегда применялись для различения между относительными строками и абсолютными строками и относительными столбцами и абсолютными столбцами, что позволяет адресовать количество столбцов, вмещающееся в 14 битов. Новое максимальное количество столбцов на каждый лист, разрешенное в Excel 2007, равно 2^14 = 16384. Команда Excel могла бы навсегда решить проблему ограничения числа строк/столбцов с помощью кодирования "переменной длины", как они сделали для структуры записи, но они решали не делать этого. Итак, обобщение:

версия Excel

кодирование строки

кодирование столбца

Excel 97-2003

2 байта

2 байта (бит 14 и 15 хранят флаги абсолютный/относительный)

Excel 2007

4 байта

2 байта (бит 14 и 15 хранят флаги абсолютный/относительный)

•    В Excel 2007 есть новые концепции, такие как возможность адресовать столбцы таблицы (или другие проекции, типа заголовков таблицы) в простых формулах. Это дает новый синтаксис, например, =SUM(Table1[2004]) вместо =SUM(range) or =SUM(A1:A3). Так как таблица в комплекте со столбцами не хранится как скрытые диапазоны, для ее представления требуется новый ptgTokens в байткоде формулы. В примере был разгадан один ptgToken = 0x18, обозначающий Table1[2004] в формуле =SUM(Table1[2004]). Если создать таблицу по имени Table1 с третьим столбцом по имени 2004, формула =SUM(Table1[2004]) хранится с помощью ptgTokens следующим образом:

    09 (2c) 05 00 00 00        column (0-based)
            00 00 00 00                         style (0-based)
            00 00 00 00 00 00 20 40             IEEE float
            10 00                               grbits (0010 = autoCalc)
            12 00 00 00                         len of formula in bytes
                        (=SUM(Table1[2004])
               18 19 00 00 01 00 01 00 00 00 02 00 02 00 ptgTokenTable
               = 18 + ... (Table1[2004])
               19 10 53 8b                      ptgTokenAttr optimized SUM
            00 00 00 00

Строки хранятся встроенными, или на них ссылаются благодаря индексу, в случае чего они в действительности хранятся в отдельной части, называемой частью общих строк. Поскольку потребитель не управляет способом хранения строк, на деле, если нужны значения ячеек, необходимо прочитать часть общих строк, построить таблицу индексированных строк перед чтением частей листа.