Dopo aver letto specs nel formato di file STL, desidero scrivere alcuni test per garantire che un file sia, in effetti, un file binario o ASCII valido.Verificare che un file STL sia ASCII o binario
Un file STL ASCII-based può essere determinato trovando il testo "solido" al byte 0, seguito da uno spazio (valore esadecimale \x20
), e poi una stringa di testo opzionale, seguito da un ritorno a capo.
Un file STL binario ha una riservata intestazione -byte, seguita da un numero intero senza segno -byte ( NumberOfTriangles) e quindi byte di dati per ciascuno dei NumberOfTriangles sfaccettature specificato.
Ogni faccetta del triangolo è byte di lunghezza: 12 galleggianti a precisione singola (4 byte) seguiti da un numero intero senza segno corto (2 byte) senza segno.
Se un file binario è esattamente 84 + NumberOfTriangles * 50 byte, può essere in genere considerato un file binario valido.
Purtroppo, i file binari può contengono il testo "solido" a partire dal byte 0 nei contenuti dell'intestazione di 80 byte. Pertanto, un test solo per quella parola chiave non può determinare in modo positivo che un file sia ASCII o binario.
Questo è quello che ho finora:
STL_STATUS getStlFileFormat(const QString &path)
{
// Each facet contains:
// - Normals: 3 floats (4 bytes)
// - Vertices: 3x floats (4 bytes each, 12 bytes total)
// - AttributeCount: 1 short (2 bytes)
// Total: 50 bytes per facet
const size_t facetSize = 3*sizeof(float_t) + 3*3*sizeof(float_t) + sizeof(uint16_t);
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
{
qDebug("\n\tUnable to open \"%s\"", qPrintable(path));
return STL_INVALID;
}
QFileInfo fileInfo(path);
size_t fileSize = fileInfo.size();
if (fileSize < 84)
{
// 80-byte header + 4-byte "number of triangles" marker
qDebug("\n\tThe STL file is not long enough (%u bytes).", uint(fileSize));
return STL_INVALID;
}
// Look for text "solid" in first 5 bytes, indicating the possibility that this is an ASCII STL format.
QByteArray fiveBytes = file.read(5);
// Header is from bytes 0-79; numTriangleBytes starts at byte offset 80.
if (!file.seek(80))
{
qDebug("\n\tCannot seek to the 80th byte (after the header)");
return STL_INVALID;
}
// Read the number of triangles, uint32_t (4 bytes), little-endian
QByteArray nTrianglesBytes = file.read(4);
file.close();
uint32_t nTriangles = *((uint32_t*)nTrianglesBytes.data());
// Verify that file size equals the sum of header + nTriangles value + all triangles
size_t targetSize = 84 + nTriangles * facetSize;
if (fileSize == targetSize)
{
return STL_BINARY;
}
else if (fiveBytes.contains("solid"))
{
return STL_ASCII;
}
else
{
return STL_INVALID;
}
}
Finora, questo ha funzionato per me, ma io sono preoccupato che byte 80 ° di un file ASCII potrebbe contenere alcuni caratteri ASCII che, quando tradotto in uint32_t, potrebbe effettivamente essere uguale alla lunghezza del file (molto improbabile, ma non impossibile).
Ci sono ulteriori passaggi che potrebbero rivelarsi utili per convalidare se posso essere "assolutamente sicuro" che un file sia ASCII o binario?
UPDATE:
Seguendo i consigli di @Powerswitch e @RemyLebeau, sto facendo ulteriori test per le parole chiave. Questo è quello che ho adesso:
STL_STATUS getStlFileFormat(const QString &path)
{
// Each facet contains:
// - Normals: 3 floats (4 bytes)
// - Vertices: 3x floats (4 byte each, 12 bytes total)
// - AttributeCount: 1 short (2 bytes)
// Total: 50 bytes per facet
const size_t facetSize = 3*sizeof(float_t) + 3*3*sizeof(float_t) + sizeof(uint16_t);
QFile file(path);
bool canFileBeOpened = file.open(QIODevice::ReadOnly);
if (!canFileBeOpened)
{
qDebug("\n\tUnable to open \"%s\"", qPrintable(path));
return STL_INVALID;
}
QFileInfo fileInfo(path);
size_t fileSize = fileInfo.size();
// The minimum size of an empty ASCII file is 15 bytes.
if (fileSize < 15)
{
// "solid " and "endsolid " markers for an ASCII file
qDebug("\n\tThe STL file is not long enough (%u bytes).", uint(fileSize));
file.close();
return STL_INVALID;
}
// Binary files should never start with "solid ", but just in case, check for ASCII, and if not valid
// then check for binary...
// Look for text "solid " in first 6 bytes, indicating the possibility that this is an ASCII STL format.
QByteArray sixBytes = file.read(6);
if (sixBytes.startsWith("solid "))
{
QString line;
QTextStream in(&file);
while (!in.atEnd())
{
line = in.readLine();
if (line.contains("endsolid"))
{
file.close();
return STL_ASCII;
}
}
}
// Wasn't an ASCII file. Reset and check for binary.
if (!file.reset())
{
qDebug("\n\tCannot seek to the 0th byte (before the header)");
file.close();
return STL_INVALID;
}
// 80-byte header + 4-byte "number of triangles" for a binary file
if (fileSize < 84)
{
qDebug("\n\tThe STL file is not long enough (%u bytes).", uint(fileSize));
file.close();
return STL_INVALID;
}
// Header is from bytes 0-79; numTriangleBytes starts at byte offset 80.
if (!file.seek(80))
{
qDebug("\n\tCannot seek to the 80th byte (after the header)");
file.close();
return STL_INVALID;
}
// Read the number of triangles, uint32_t (4 bytes), little-endian
QByteArray nTrianglesBytes = file.read(4);
if (nTrianglesBytes.size() != 4)
{
qDebug("\n\tCannot read the number of triangles (after the header)");
file.close();
return STL_INVALID;
}
uint32_t nTriangles = *((uint32_t*)nTrianglesBytes.data());
// Verify that file size equals the sum of header + nTriangles value + all triangles
if (fileSize == (84 + (nTriangles * facetSize)))
{
file.close();
return STL_BINARY;
}
return STL_INVALID;
}
Sembra di gestire più casi limite, e ho tentato di scrivere in un modo che gestisce estremamente grandi (da pochi gigabyte) file STL con garbo senza richiedere la INTERO file da caricare in memoria in una sola volta per la scansione per il testo "endsolid".
Sentiti libero di fornire commenti e suggerimenti (soprattutto per le persone in cerca di soluzioni future).
L'articolo di Wikipedia dice che * Un file STL binario ha un header di 80 caratteri (che è generalmente ignorata, ma non dovrebbe mai cominciare con "solido", perché che porterà la maggior parte dei software presuppone che si tratti di un file STL ASCII) * –
Vero. Ma nel testare i file STL casuali scaricati da luoghi come [Thingiverse] (http://www.thingiverse.com), molti file STL binari effettivamente iniziano con "solido" anche se "non dovrebbero". Ecco una ragione per cui controllare quei primi 5-6 byte non è sempre una garanzia. – OnlineCop