Как определить формат файла excel?

138
20 апреля 2021, 11:20

Если не вдаваться в подробности, попадаются файлы .xlsx которые названы как .xls и наоборот, .xls которые названы как .xlsx. Так же есть такие которые соответствуют своим расширением.

В общем надо пройтись по файлам и переименовать их правильно, но я не нашел как определить формат файла не опираясь на расширение в его названии

Answer 1

По шапке. Файлы нового экселя xlsx начинаются PK - сигнатурой zip-а 50 4B. С такой сигнатурой могут быть не только xlsx а ещё и docx да и zip архивы, нужно вычитывать список упакованых файлов, для уточнения.

Файлы .xls - бывают двух типов, "новые" xls (обычно именно такие встречаются). Они имеют сигнатуру D0 CF 1 E0 A1 B1 1A E1 (на самом деле это сигнатура документа офис, для полее подробной идентификации, т.е. doc и xls будут иметь одинаковую сигнатуру, нужно искать "главное" вложение в biff обвертке, и проверять там сигнатуру, либо по списку файлов biff-обвертки), и соовсем старые (до 7 версии, 6-тая не была в обиходе) имеют сигнатуру 09 08

Например можно так

 byte[] xlsDoc = f(); // Загрузили документ в байты
 if ((xlsDoc[0]==0x50) && (xlsDoc[1]==0x4B)) {
     // Новый ексель
      } 
 else if ((xlsDoc[0]==0xD0) && (xlsDoc[1]==0xCF) /*...*/) {
    // Офис 97
 }
 else if ((xlsDoc[0]==9) && (xlsDoc[1]==8)) {
    // Старый одностраничный
 } else {
    // В других случаях - думаю что мусор
 }

Т.к. файл может быть большой, то лучше вычитать первые 16 байт, и их проанализировать, а потом вернуть Stream.Position в ноль.

Ещё с excel мне попадались приколы

  • xml <?xml - файл с excel (если в excel сохранить файл как xml)
  • html excel <html - часто советуют такой файл делать в интернете javascipt-ом php и т п, - таблица с <td> <tr> тегами и определенными приписками.

Ссылки на сигнатуры

  1. http://uk.wikipedia.org/wiki/%D0%A1%D0%B8%D0%B3%D0%BD%D0%B0%D1%82%D1%83%D1%80%D0%B0_%D1%84%D0%B0%D0%B9%D0%BB%D1%83_(%D0%BF%D0%B5%D1%80%D0%B5%D0%BB%D1%96%D0%BA)
  2. http://www.filesignatures.net/index.php?page=search&search=XLS&mode=EXT
  3. http://www.filesignatures.net/index.php?page=search&search=XLSX&mode=EXT
  4. http://en.wikipedia.org/wiki/Microsoft_Excel
Answer 2

Как вариант, в Windows для определения, является ли файл корректным XLS-файлом, можно использовать Structured Storage API. Согласно спецификации, формат XLS - это файл формата Structured Storage, который содержит поток с именем Workbook.

MS-XLS: Excel Binary File Format Structure, пункт 2.1.2:

A file of the type specified by this document consists of storages and streams as specified in [MS-CFB]...

A workbook MUST contain the workbook stream...

Можно использовать следующий код для проверки на XLS, на основе этого правила:

using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("ole32.dll")]
        static extern int StgOpenStorageEx(
            [MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
            uint grfMode,
            uint stgfmt,
            uint grfAttrs,
            IntPtr pStgOptions,
            IntPtr reserved2,
            [In] ref Guid riid,
            out IStorage ppObjectOpen);
        const uint STGM_DIRECT = 0;
        const uint STGM_READ = 0;        
        const uint STGM_SHARE_EXCLUSIVE = 0x10;        
        const uint STGFMT_STORAGE = 0;        
        const uint PID_FIRST_USABLE = 2;
        const uint STGC_DEFAULT = 0;               
        [Guid("0000000B-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IStorage
        {
            void a();
            
            [PreserveSig]
            int OpenStream(string pwcsName, 
                IntPtr reserved1, 
                uint grfMode, 
                uint reserved2, 
                [MarshalAs(UnmanagedType.Interface)] out object ppstm);
            
            void CreateStorage(string pwcsName, uint grfMode,  uint reserved1, uint reserved2, out IStorage ppstg);            
            void OpenStorage(string pwcsName, IStorage pstgPriority,  uint grfMode,  IntPtr snbExclude, uint reserved, out IStorage ppstg);            
            void CopyTo( uint ciidExclude, Guid[] rgiidExclude,  IntPtr snbExclude, IStorage pstgDest);            
            void MoveElementTo(string pwcsName, IStorage pstgDest, string pwcsNewName, uint grfFlags);            
            void Commit( uint grfCommitFlags);            
            void Revert();            
            void b();            
            void DestroyElement(string pwcsName);            
            void RenameElement(string pwcsOldName, string pwcsNewName);            
            void c();            
            void SetClass( ref Guid clsid);            
            void SetStateBits( uint grfStateBits,  uint grfMask);            
            void d();
        }
        public static bool IsXLS(string path)
        {            
            IStorage pStorage = null;
            object o = null;
            int hr;            
            Guid guidStorage = typeof(IStorage).GUID;            
            try
            {
                //открываем файл
                hr = StgOpenStorageEx(path, STGM_READ | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE,
                    0, IntPtr.Zero, IntPtr.Zero, ref guidStorage, out pStorage);
                if (hr != 0) return false; //NOT Structured storage file                 
                       
                //открываем поток
                hr = pStorage.OpenStream("Workbook", IntPtr.Zero, STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE, 0, out o);
                                            
                return hr == 0;
            }
            finally
            {
                //освобождение ресурсов                
                if (pStorage != null) Marshal.ReleaseComObject(pStorage);
                if (o != null) Marshal.ReleaseComObject(o);
            }
        }       
    }  
}

Так как файл XLSX является ZIP-архивом определенной структуры, можно применить для проверки ту же логику, и воспользоваться любой библиотекой для работы с ZIP-архивами (в .NET 4.5+ есть встроенная System.IO.Compression).

READ ALSO
Конвертирование string в int c#

Конвертирование string в int c#

Есть стринговое значение:

112
Unity 2019.1 компиляция в apk

Unity 2019.1 компиляция в apk

Не компилируется в apk, ошибка CommandInvokationFailure: Gradle build failedРаньше решалось это в настройке Build System - надо было Gradle на Internal поменять, а теперь в новой...

127
Плавное перемещение usercontrol по области Grid&#39;a [WPF]

Плавное перемещение usercontrol по области Grid'a [WPF]

Поискал по сайту и ответа на мой вопрос я не нашел, поэтому решил создать новую темуУ меня есть полноэкранный Grid в котором находится usercontrol...

87
Как объединить три массива в один?

Как объединить три массива в один?

У меня есть три массиваПервый

116