我有一个包含数百万个字节的数组。这些字节是int值(Int16,Int24或Int32)。现在,我要从字节数中获取具有最大int值的x字节。
因此,为了更好地解释这一点,让我们想象一个包含10个条目的数组:
byte[] arr = {255, 10, 55, 60, 128, 90, 88, 66, 199, 56};
我会知道我们是否使用In16,Int24或Int32,因此对于此示例,假设我们正在使用Int16。这意味着,我们使用2个字节来表示一个Int16。因此Ints包括:
{255, 10},
{55, 60},
{128, 90},
{88, 66},
{199, 56}
问题1:因为这是音频处理所必需的,所以1046低于-2096。因此,需要独立于否定性进行比较
问题2:因为这需要非常有效,所以将字节转换为Ints进行比较似乎效率低下,应该有另一种方法。
这是起点:
/// <summary>
/// Gets the maximum value of a number of bytes representing Int-Values
/// </summary>
/// <returns>The channels.</returns>
/// <param name="leftChannel">Left channel.</param>
/// <param name="rightChannel">Right channel.</param>
/// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
/// <param name="countBytesToCombine">The number of bytes to look for the highest value</param>
private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countBytesToCombine)
{
}
/// <summary>
/// Gets the highest byte[] value
/// </summary>
/// <returns>The highest value. The size of the byte array is equal the bytesPerInt</returns>
/// <param name="bytes">A subarray of the given byte array of the upper method. The size of this array is equals countBytesToCombine</param>
/// <param name="bytesPerInt">The count of bytes representing an Int</param>
private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
{
}
这是一个可行的解决方案,但是执行每个通道的1400万字节大约需要2秒钟的时间,这太过分了。
/// <summary>
/// Gets the maximum value of a number of bytes representing Int-Values
/// </summary>
/// <returns>The channels.</returns>
/// <param name="leftChannel">Left channel.</param>
/// <param name="rightChannel">Right channel.</param>
/// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
/// <param name="countValuesToCombine">The number of bytes to look for the highest value</param>
private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
{
var cLeft = new List<byte>();
var cRight = new List<byte>();
for (int i = 0; i < leftChannel.Length; i += countValuesToCombine * bytesPerInt)
{
var arrLeft = SubArray(leftChannel, i, countValuesToCombine * bytesPerInt);
var arrRight = SubArray(rightChannel, i, countValuesToCombine * bytesPerInt);
cLeft.AddRange(GetHighestValue(arrLeft, bytesPerInt));
cRight.AddRange(GetHighestValue(arrRight, bytesPerInt));
}
return (cLeft.ToArray(), cRight.ToArray());
}
/// <summary>
/// Gets the highest byte[] value
/// </summary>
/// <returns>The highest value.</returns>
/// <param name="bytes">Bytes.</param>
/// <param name="bytesPerInt">The count of bytes representing an Int</param>
private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
{
byte[] bytesOfHighestValue = new byte[bytesPerInt];
for (int i = 0; i < bytes.Length; i += bytesPerInt)
{
var arr = SubArray(bytes, i, bytesPerInt);
if (IsValueHigher(arr, bytesOfHighestValue, bytesPerInt))
{
bytesOfHighestValue = arr;
}
}
return bytesOfHighestValue;
}
private bool IsValueHigher(byte[] one, byte[] two, int bytesPerInt)
{
var o = ConvertToInt(one, bytesPerInt);
var t = ConvertToInt(two, bytesPerInt);
return Math.Abs(o) > Math.Abs(t);
}
private int ConvertToInt(byte[] bytes, int bytesPerInt)
{
switch (bytesPerInt)
{
case 2:
return BitConverter.ToInt16(bytes, 0);
case 3:
return Int24.ToInt32(bytes, 0);
case 4:
return BitConverter.ToInt32(bytes, 0);
}
return 0;
}
这很难解释,因此请在投票之前询问是否有疑问。
答案 0 :(得分:1)
好吧,这是4个字节整数的简单实现:
private static int GetHighestValue(byte[] data)
{
if (data.Length % 4 != 0)
throw new ArgumentException();
var maximum = 0, maximumAbs = 0;
for (var i = 0; i < data.Length; i+=4)
{
var current = BitConverter.ToInt32 (data, i);
var currentAbs = Math.Abs(current);
if (currentAbs > maximumAbs)
{
maximum = current;
maximumAbs = currentAbs;
}
}
return maximum;
}
在byte[]
上以1百万个字节运行此代码,用Debug进行编译大约需要3毫秒。
我不知道您要针对哪种速度,但对于99%的情况,这应该没问题。
编辑:由于您更新了问题并包含示例代码,因此此更新为
这些是我在某些方面使您的代码慢于需要的地方:
我们不需要在CombineChannels
的每次迭代中都创建子数组。我们可以重写GetHighestValue
,以使其以array
,offset
和amount
作为参数。
与其使用一种CombineChannels
方法,不如将其拆分为不同的字节大小。例如CombineChannelsInt32
,CombineChannelsInt16
...这样,方法本身可以将最大值存储为int32
/ int16
/ ...,而不必在每次迭代时都进行转换。
所以这是我们最终会得到如下结果的方法:
(byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
{
switch(bytesPerInt)
{
case 2:
return CombineChannelsInt16(leftChannel, rightChannel, countValuesToCombine);
case 3:
return CombineChannelsInt24(leftChannel, rightChannel, countValuesToCombine);
case 4:
return CombineChannelsInt32(leftChannel, rightChannel, countValuesToCombine);
}
}
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt16(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt24(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt32(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
short GetHighestValueInt16(byte[] bytes, int offset, int amount);
Int24 GetHighestValueInt24(byte[] bytes, int offset, int amount);
int GetHighestValueInt32(byte[] bytes, int offset, int amount);
答案 1 :(得分:0)
我做了一个返回最大索引的方法。它首先比较高字节,当相等时则比较低字节。使用更大的整数,它的运行速度甚至更快。
static int getMaxIndex(byte[] data, int byteLenght)
{
int MaxIndex = 0;
int signMax = data[byteLenght - 1] >> 7;// get sign
for (int i = byteLenght; i < data.Length; i += byteLenght)
{
int step = byteLenght - 1;
int compResult = 0;
while (compResult == 0 && step > -1)
{
if (step == byteLenght -1)
{
int signData = data[i + step] >> 7;
compResult = signData - signMax;
if (compResult == 0) compResult = data[MaxIndex + step] & 127 - data[i + step] & 127;
}
else compResult = data[MaxIndex + step] - data[i + step];
if (compResult < 0)
{
MaxIndex = i;
signMax = data[MaxIndex + step] >> 7;
}
step--;
}
}
return MaxIndex;
}
答案 2 :(得分:0)
正如已经提到过几次,请避免在阅读内容中出现“ if”语句;只需为读取Int16
,Int24
和Int32
做一个单独的功能,然后预先选择要使用的功能即可。
我个人会使用System.IO.BinaryReader
;它已经包含用于从流中读取整数的函数,并且与BitConverter
(从技术上取决于系统字节序)不同,BinaryReader
实际上可以保证读取的值是little-endian。它在MSDN规范中。
这是使用BinaryReader
的基本功能,以Int32
为例。在此版本中,我让EndOfStreamException
负责结局。他们说异常抛出/处理是一项繁重的操作,但是在这种情况下,它替换了两次读取之间的 lot 检查,因此可能是有道理的。
您可以通过对流指针进行实际检查来替换while (true)
来适应这种情况。要么只是根据输入字节数组的长度检查ms.Position,要么跟踪自己变量中的位置,就可以在每一步中增加读取的字节数。
public static Int32 GetHighestValueInt32(Byte[] bytes)
{
Int32 maxval = 0;
try
{
using (MemoryStream ms = new MemoryStream(bytes))
using (BinaryReader reader = new BinaryReader(ms))
{
while (true)
{
// Clear highest bit so the value's always a positive Int32.
Int32 val = (Int32)(reader.ReadUInt32() & 0x7FFFFFFF);
if (val > maxval)
maxval = val;
}
}
}
catch (EndOfStreamException ex)
{
// Finished reading!
}
return maxval;
}
对于Int16
,实际的行val
应该简单地替换为
Int16 val = (Int16)(reader.ReadUInt16() & 0x7FFF);
同样,maxval
和返回类型也应更改为Int16
。
BinaryReader
本身无法从流中读取Int24
。但是,解决方法并不难。您可以简单地使用Int32
并将其下移8位,然后手动调整流指针以补偿两个额外的读取字节:
while (true)
{
Int32 val = (Int32)((reader.ReadUInt32() >> 8) & 0x7FFFFF);
ms.Position -= 2;
if (val > maxval)
maxval = val;
}