После прочтения спецификаций в формате файла STL, я хочу написать несколько тестов, чтобы убедиться, что файл является фактически действующим двоичным или ASCII файлом.
Файл STL, основанный на ASCII, может быть определен путем нахождения текста " solid" в байте 0, за которым следует пробел (шестнадцатеричное значение \x20
), а затем необязательная текстовая строка, затем по новой строке.
В двоичном файле STL есть зарезервированный заголовок 80 -byte, за которым следует 4 -byte беззнаковое целое число (NumberOfTriangles), а затем 50 байтов данных для каждой из факетов NumberOfTriangles.
Каждый грань треугольника имеет длину 50: 12 одноточечных (4 байтовых) полях с последующим беззнаковым коротким (2 байтовым) целым без знака.
Если двоичный файл в точности длиннее 84 + NumberOfTriangles * 50, его обычно можно считать действительным двоичным файлом.
К сожалению, двоичные файлы могут содержать текст " solid", начиная с байта 0 в содержимом 80-байтового заголовка. Поэтому тест только для этого ключевого слова не может положительно утверждать, что файл является ASCII или двоичным.
Это то, что у меня есть до сих пор:
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;
}
}
До сих пор это работало для меня, но я беспокоюсь, что простой 80-байтовый файл ASCII файла может содержать некоторые символы ASCII, которые при переводе на uint32_t могут фактически равняться длине файла (очень маловероятно, но не невозможно).
Существуют ли дополнительные шаги, которые могут оказаться полезными при проверке того, могу ли я быть абсолютно уверенным в том, что файл является либо ASCII, либо двоичным?
UPDATE:
Следуя советам @Powerswitch и @RemyLebeau, я делаю дополнительные тесты для ключевых слов. Это то, что у меня есть сейчас:
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;
}
Кажется, что он обрабатывает больше случаев ребер, и я попытался написать его таким образом, чтобы изящно обрабатывать чрезвычайно большие (несколько гигабайтных) STL файлов, не требуя, чтобы файл ENTIRE загружался в память сразу для его сканирования для текста "endolid".
Не стесняйтесь предоставлять любые отзывы и предложения (особенно для людей, которые в будущем ищут решения).