在讀取STL文件格式的specs之後,我想編寫一些測試以確保文件實際上是有效的二進制文件或ASCII文件。驗證STL文件是ASCII還是二進制
基於ASCII的STL文件可以通過尋找文本「固體」在字節0,後跟一個空格(十六進制值\x20
),然後任選的文本串,然後換行來確定。
二進制STL文件具有保留字節頭,後跟一個字節無符號整數( NumberOfTriangles),並且對於每個 NumberOfTriangles刻面然後個字節的數據指定。
每個三角形方面是字節的長度:12個單精度(4字節)浮點數,後跟無符號短(2字節)無符號整數。
如果二進制文件正好是84 + NumberOfTriangles * 50字節長,通常可以認爲它是有效的二進制文件。
不幸的是,二進制文件可以包含文本「固體」,在80字節的報頭的內容開始於字節0。因此,僅對該關鍵字進行測試並不能確定文件是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;
}
}
到目前爲止,這爲我工作,但我擔心的是一個普通的ASCII文件的第80個字節可能包含一些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文件一次加載到內存中,以便掃描「endsolid」文本。
隨時提供任何反饋和建議(特別是在未來尋找解決方案的人)。
維基百科的文章說,*一個二進制STL文件有80個字符的標題(這通常被忽略,但不應該與「實」開始,因爲這將導致大多數軟件假設這是一個ASCII STL文件)* – 2014-10-03 00:14:44
是真的。但是在測試從[Thingiverse](http://www.thingiverse.com)等地下載的隨機STL文件時,許多二進制STL文件實際上都以「固定」開頭,即使它們「不應該」。所以這是檢查那些第一個5-6字節並不總是保證的原因之一。 – OnlineCop 2014-10-03 16:15:49