使用 C++ 读取文件时切换字节序的最快方法是什么?
What's the fastest way to switch endianness when reading from a file with C++?
有人向我提供了一个二进制文件供我读取,其中包含一系列原始值。为了简单起见,假设它们是无符号整数值,4 字节或 8 字节长。对我来说不幸的是,这些值的字节顺序与我的处理器的字节顺序不兼容(小与大或反之亦然;不要介意奇怪的 PDF 字节顺序等);我希望内存中的这些数据具有正确的字节顺序。
考虑到我正在从文件中读取数据,最快的方法是什么?如果不值得利用这个事实,请解释原因。
考虑到您正在从文件中读取数据这一事实,与文件 IO 相比,切换字节序的方式对运行时的影响微不足道。
可能会产生重大影响的是您读取数据的方式。尝试乱序读取字节不是一个好主意。只需按顺序读取字节,然后切换字节序。这将读取和字节交换分开。
我通常希望从字节交换代码中得到什么,当然在读取文件的情况下,它适用于任何字节顺序并且不依赖于特定的体系结构说明。
char* buf = read(); // let buf be a pointer to the read buffer
uint32_t v;
// little to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
v |= buf[i] << CHAR_BIT * i;
// big to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
v |= buf[i] << CHAR_BIT * (sizeof v - i);
无论本机是大、小还是中间字节序类型之一,这都适用。
当然,boost has already implemented these for you, so there is no need to re-implement. Also, there are the ntoh?
函数族由 POSIX 和 windows C 库提供,可用于转换大端 to/from 本机。
不是最快的,但是一种可移植的方法是将文件读入一个(无符号的)int 数组,将 int 数组别名为 char one(根据严格的别名规则允许)并交换内存中的字节。
完全便携的方式:
swapints(unsigned int *arr, size_t l) {
unsigned int cur;
char *ix;
for (size_t i=0; i<l; i++) {
int cur;
char *dest = static_cast<char *>(&cur) + sizeof(int);
char *src = static_cast<char *>(&(arr[i]));
for(int j=0; j<sizeof(int); j++) *(--dest) = *(src++);
arr[i] = cur;
}
}
但是如果您不需要便携性,有些系统会提供交换功能。例如 BSD 系统有 bswap16
、bswap32
和 bswap64
分别在 uint16_t
、uint32_t
和 uint_64_t
中交换字节。毫无疑问,Microsoft 或 GNU-Linux 世界中存在等效功能。
或者,如果您知道该文件是 网络 顺序(big endian)而您的处理器不是,您可以使用 ntohs
和 ntohl
函数分别用于 uint16_t
和 uint32_t
.
备注(根据 AndrewHenle 的评论):无论主机字节序如何,始终可以使用 ntohs
和 ntohl
- 只是它们在大端系统上是无操作的
有人向我提供了一个二进制文件供我读取,其中包含一系列原始值。为了简单起见,假设它们是无符号整数值,4 字节或 8 字节长。对我来说不幸的是,这些值的字节顺序与我的处理器的字节顺序不兼容(小与大或反之亦然;不要介意奇怪的 PDF 字节顺序等);我希望内存中的这些数据具有正确的字节顺序。
考虑到我正在从文件中读取数据,最快的方法是什么?如果不值得利用这个事实,请解释原因。
考虑到您正在从文件中读取数据这一事实,与文件 IO 相比,切换字节序的方式对运行时的影响微不足道。
可能会产生重大影响的是您读取数据的方式。尝试乱序读取字节不是一个好主意。只需按顺序读取字节,然后切换字节序。这将读取和字节交换分开。
我通常希望从字节交换代码中得到什么,当然在读取文件的情况下,它适用于任何字节顺序并且不依赖于特定的体系结构说明。
char* buf = read(); // let buf be a pointer to the read buffer
uint32_t v;
// little to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
v |= buf[i] << CHAR_BIT * i;
// big to native
v = 0;
for(unsigned i = 0; i < sizeof v; i++)
v |= buf[i] << CHAR_BIT * (sizeof v - i);
无论本机是大、小还是中间字节序类型之一,这都适用。
当然,boost has already implemented these for you, so there is no need to re-implement. Also, there are the ntoh?
函数族由 POSIX 和 windows C 库提供,可用于转换大端 to/from 本机。
不是最快的,但是一种可移植的方法是将文件读入一个(无符号的)int 数组,将 int 数组别名为 char one(根据严格的别名规则允许)并交换内存中的字节。
完全便携的方式:
swapints(unsigned int *arr, size_t l) {
unsigned int cur;
char *ix;
for (size_t i=0; i<l; i++) {
int cur;
char *dest = static_cast<char *>(&cur) + sizeof(int);
char *src = static_cast<char *>(&(arr[i]));
for(int j=0; j<sizeof(int); j++) *(--dest) = *(src++);
arr[i] = cur;
}
}
但是如果您不需要便携性,有些系统会提供交换功能。例如 BSD 系统有 bswap16
、bswap32
和 bswap64
分别在 uint16_t
、uint32_t
和 uint_64_t
中交换字节。毫无疑问,Microsoft 或 GNU-Linux 世界中存在等效功能。
或者,如果您知道该文件是 网络 顺序(big endian)而您的处理器不是,您可以使用 ntohs
和 ntohl
函数分别用于 uint16_t
和 uint32_t
.
备注(根据 AndrewHenle 的评论):无论主机字节序如何,始终可以使用 ntohs
和 ntohl
- 只是它们在大端系统上是无操作的