I have some old C# code that does what I need, with the file type I'm working with, but I need to get it into Java. I've been reading up on binary I/O but I can't figure out how to deal with the header and I don't understand the C# code enough to know what it's doing
I would appreciate any assistance - mostly with understanding what the C# code means when it uses br.readInt32() and such and how to emulate that with Java which (as I understand it) reads the binary differently
I don't understand binary files very well (nor do I want to, this is a one off code piece), I just want to get the data out then I can work on the code that I understand better.
thanks
C# snippet:
[code]
public void ConvertEVDtoCSV(string fileName)
{
string[] fileArray = File.ReadAllLines(fileName);
float minX = 0;
float maxX = 0;
try
{
FileStream fs = new FileStream(fileName, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
/*
16 + n*80*6 = sizeof(header) where n is the 9th nibble of the file (beginning of the 5th byte)
*/
//Reads "EVIS"
br.ReadBytes(4);
//Reads numDataSets
int numDataSets = br.ReadInt32();
//Reads lngNumPlotSurfaces
int lngNumPlotSurfaces = br.ReadInt32();
//Reads headerEvisive length
int headerEvisive = br.ReadInt32();
//skip all six title and axes text lines.
int remainingHeader = (lngNumPlotSurfaces * 6 * 80) + headerEvisive;
br.ReadBytes(remainingHeader); //could also use seek(remainingHeader+16), but streams don't support seek?
long dataSize = numDataSets * (2 + lngNumPlotSurfaces); //meb 6-8-2016: +2 for X and Y
string[] dataForCSVFile = new string[dataSize];
for (long cnt = 0; cnt < numDataSets; cnt++)
{
for (int j = 0; j < 2 + lngNumPlotSurfaces; j++) //+2 for X and Y
{
//don't read past the end of file
if (br.BaseStream.Position<br.BaseStream.Length) {
//This is where the data needs to be read in and converted from 32-bit single-precision floating point to strings for the csv file
float answerLittle = br.ReadSingle();
if (j == 0 && answerLittle > maxX)
maxX = answerLittle;
if (j == 0 && answerLittle < minX)
minX = answerLittle;
if (j > lngNumPlotSurfaces)
dataForCSVFile[cnt * (2 + lngNumPlotSurfaces) + j] = answerLittle.ToString() + "\r\n";
else
dataForCSVFile[cnt * (2 + lngNumPlotSurfaces) + j] = answerLittle.ToString() + ",";
}
}
}
fs.Close();
textBox_x_max.Text = (maxX).ToString("F2");
textBox_x_min.Text = (minX).ToString("F2");
StreamWriter sw = new StreamWriter(tempfile);
for (int i = 0; i < dataForCSVFile.Length; i++)
{
sw.Write(dataForCSVFile[i]);
}
sw.Close();
}
catch (Exception ex)
{ Console.WriteLine("Error reading data past eof."); }
}
From MSDN:
https://msdn.microsoft.com/en-us/library/system.io.binaryreader.readint32(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes."
https://msdn.microsoft.com/en-us/library/system.io.binaryreader.readsingle(v=vs.110).aspx
A 4-byte floating point value read from the current stream.
For reading the integer in Java, you will also have to pay attention to endianness. C# BinaryReader is small endian by default, while Java is big endian. So when reading integers you will have to read 4 bytes, swap their order and recombine them.
Related
My assignment is to convert binary to decimal in a JLabel array without using pre-written methods (there's no user input). I have the right idea but for some reason the output is always a little bit off. I've gone through it countless times but I can't find anything wrong with my algorithm and I'm very confused as to why it doesn't produce the correct answer. I'd be very grateful if someone could help me out. Thanks!
Side note: I've read similar discussion threads regarding binary to decimal conversions but I don't understand how to do it with arrays.
Here is a snippet of my code:
private void convert()
{
int[] digit = new int[8]; //temporary storage array
int count = 0;
for(int x = 0; x < digit.length; x++)
{
digit[x] = Integer.parseInt(bits[x].getText()); //bits is the original array
count= count + digit[digit.length - 1 - x] * (int)(Math.pow(2, x));
}
label.setText("" + count);
}
You are following the binary number from left to right but are grabbing the wrong digit. You want the same digit but to multiply by the right power of two - first index being +n*128 and not +n*1
int count = 0;
for(int i = 0; i < bits.length; i++) {
count += Integer.parseInt(bits[i].getText()) * Math.pow(2, bits.length - i - 1);
}
Obviously there is a bug in your snippet.
You set the digit[x], but not set the digit[length - 1 - x].
for example, x = 0, you set the digit[0], but not set digit[7].
So there will be an error when you want use the digit[length - 1 -x] here :
count= count + digit[digit.length - 1 - x] * (int)(Math.pow(2, x));
This the correct code here:
private void convert()
{
int count = 0, length = 8;
for(int i = 0; i < length; count += Integer.parseInt(bits[i].getText()) * (1 << (length - 1 - i)), i++);
label.setText("" + count);
}
Have not test the code. But I think it will work.
My conversion code is working fine, however I can't seem to restrict the user from inputting numbers other than 0 and 1. I've managed to deal with it to a certain extent: the program recognizes the number (for example 2), but it continues converting numbers that come after it simply ignoring the 2 itself and moving on. If I enter 2011, it's going to convert it to 3 in decimal (011 = 3), instead of breaking completely. I'm assuming it has something to do with the first condition, but I don't know how to fix it, so any help is greatly appreciated.
I'm working in JavaFX, hence the usage of the textfield (t3).
int decimal = 0;
int power = 0;
int bin = Integer.parseInt(t3.getText());
while (bin != 0) {
int temp = bin%10;
if(temp > 1) {
break;
} else {
bin = bin / 10;
decimal += temp * Math.pow(2, power);
power++;
}
}
You can specify the radix in the parseInt call
int bin = Integer.parseInt(t3.getText(), 2);
which will parse a binary number and throw a NumberFormatException on illegal input.
Instead of just break out of look if number is greater than 1, throw NumberFormatException.
int decimal = 0;
int power = 0;
int bin = Integer.parseInt("11");
while (bin != 0) {
int temp = bin%10;
if(temp > 1) {
throw new NumberFormatException("Invalid input"); //throw exception if input contains number greater than 0
} else {
bin = bin / 10;
decimal += temp * Math.pow(2, power);
power++;
}
}
I found that a similar question has been asked before here : how does Float.toString() and Integer.toString() works?
But this doesn't speak about how that function internally works. When I opened the internally source code of Integer.toString(), it is not understandable for normal junior java programmer.
Can somebody please explain what happens internally in short description ?
NOTE : This was one of the interview questions that I was asked recently. I had no idea about how to answer such question !
The no arg call of integer.toString() simply calls the static method Integer.toString(int i) (using the integer variables own primitive value), which is implemented as below;
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(0, size, buf);
}
First it checks whether it's value is == the lowest possible integer, and returns that if it is equal. If not, then it checks what size the String needs to be using the stringSize() method of Integer to use as the size of an array of characters.
stringSize() implementation below;
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
Once it has a char[] of the correct size, it then populates that array using the getChars() method, implemented below;
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
Explaining each individual step would take far too long for for a stackoverflow answer. The most pertinent section however (as pointed out in the comments) is the getChars() method which, complicated bit shifting aside, is essentially process of elimination for finding each character. I am afraid I can't go into any greater detail than that without going beyond my own understanding.
I got a WAV (32 bit sample size, 8 byte per frame, 44100 Hz, PCM_Float), which in need to create a sample array of. This is the code I have used for a Wav with 16 bit sample size, 4 byte per frame, 44100 Hz, PCM_Signed.
private float[] getSampleArray(byte[] eightBitByteArray) {
int newArrayLength = eightBitByteArray.length
/ (2 * calculateNumberOfChannels()) + 1;
float[] toReturn = new float[newArrayLength];
int index = 0;
for (int t = 0; t + 4 < eightBitByteArray.length; t += 2) // t+2 -> skip
//2nd channel
{
int low=((int) eightBitByteArray[t++]) & 0x00ff;
int high=((int) eightBitByteArray[t++]) << 8;
double value = Math.pow(low+high, 2);
double dB = 0;
if (value != 0) {
dB = 20.0 * Math.log10(value); // calculate decibel
}
toReturn[index] = getFloatValue(dB); //minorly important conversion
//to normalized values
index++;
}
return toReturn;
}
Obviously this code cant work for the 32bits sample size Wav, as I have to consider 2 more bytes in the first channel.
Does anybody know how the 2 other bytes have to be added (and shiftet) to calculate the amplitude? Unfortunately google didnt help me at all :/.
Thanks in advance.
Something like this should do the trick.
for (int t = 0; t + 4 < eightBitByteArray.length; t += 4) // t+4 -> skip
//2nd channel
{
float value = ByteBuffer.wrap(eightBitByteArray, t, 4).order(ByteOrder.LITTLE_ENDIAN).getFloat();
double dB = 0;
if (value != 0) {
dB = 20.0 * Math.log10(value); // calculate decibel
}
toReturn[index] = getFloatValue(dB); //minorly important conversion
//to normalized values
index++;
}
On another note - converting instantaneous samples to dB is nonsensical.
I'm trying to convert an base 10 number to a base 2 and back to base 10. It works only for positive argument_decimal
argument_binary = Integer.toBinaryString(argument_decimal);
back_converted_argument_decimal = Integer.valueOf(argument_binary, 2);
For argument_decimal beeing negative, I get "java.lang.NumberFormatException: For input string: "11111111111111111111111111111111""
EDIT: here is what I do:
latitude_binary = Integer.toBinaryString((int)(latitude_decimal * 1000000));
back_converted_latitude_decimal = Long.parseLong(latitude_binary, 2) / 1000000.0;
which gives me bad results like -1.1 being forth and back converted to 4293.867296
Try to go via a long:
String binary = Integer.toBinaryString(-1);
long l = Long.parseLong(binary, 2);
int i = (int) l;
Tested, and working.
Why this works is because -1 is represented as a sequence of 32 bits 1 in system memory. When using the toBinaryString method, it creates a string using that exact representation. But, 32 bits of one is in fact equal to 2^32 - 1. That is too large for an int (4 bytes), because an int goes from [-2^31, 2^31-1]. This is because the most left bit is representing the sign. So to fix that overflow, first interpret that sequence of 1 and 0 characters as a Long. A long will do because the maximum value for a long is 2^63-1. Then convert the long to an int. This is done by simply taking the lower 32 bits.
The bug in your code is that you didn't cast the Long.parseLong to an int. So this should work:
lat_bin = Integer.toBinaryString((int)(lat_dec * 1000000));
lat_dec_conv = ((int) Long.parseLong(lat_bin, 2)) / 1000000.0;
public static void convertStringToDecimal(String binary) {
int decimal = 0;
int power = 0;
if (binary.charAt(0) == '1' && binary.length() == 32) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < binary.length(); i++) {
builder.append((binary.charAt(i) == '1' ? '0' : '1'));
}
while (binary.length() > 0) {
int temp = Integer
.parseInt(builder.charAt((binary.length()) - 1)+"");
decimal += temp * Math.pow(2, power++);
binary = binary.substring(0, binary.length() - 1);
}
System.out.println((decimal + 1) * (-1));
} else {
while (binary.length() > 0) {
int temp = Integer
.parseInt(binary.charAt((binary.length()) - 1) + "");
decimal += temp * Math.pow(2, power++);
binary = binary.substring(0, binary.length() - 1);
}
System.out.println(decimal);
}
}