I'm trying to convert a hexadecimal string to string with Windows-1251 encoding. I need to use JavaScript. I've tried using this sample that someone posted:
var win1251 = new TextDecoder("windows-1251");
for (var i = 0x00; i < 0xFF; i++) {
var hex = (i <= 0x0F ? "0" : "") + i.toString(16).toUpperCase();
decodeMap[hex] = win1251.decode(Uint8Array.from([i]));
}
but I can't seem to make it work. Can someone please help out?
Propably you're using it in incorrect way.
The code in you post creates dictionary (hex => windows-1251).
So, to translate hex string to windows-1251 string you have to:
split your hex string into array with 2-hex elements
Translate every element to windows-1251
Join result array into string
var decodeMap = {};
var win1251 = new TextDecoder("windows-1251");
for (var i = 0x00; i < 0xFF; i++) {
var hex = (i <= 0x0F ? "0" : "") + i.toString(16).toUpperCase();
decodeMap[hex] = win1251.decode(Uint8Array.from([i]));
}
var number = "3230313830363131313535303435303831";
var hexarr = number.toUpperCase().match(/[0-9A-F]{2}/g)
var win1251arr = hexarr.map(el=> decodeMap[el]);
console.log(win1251arr.join(""))
You can also do it in much shorter way (directly translate your hex string without creating dictionary):
var win1251 = new TextDecoder("windows-1251");
var number = "3230313830363131313535303435303831";
var arr = number.toUpperCase().match(/[0-9A-F]{2}/g).map(el=>"0x"+el);
var uarr = Uint8Array.from(arr);
console.log(win1251.decode(uarr));
If your browser doesn't support TextDecoder, you can use dictionary from first snippet:
var decodeMap = getHexToWin1251Dictionary();
var number = "3230313830363131313535303435303831";
var hexarr = number.toUpperCase().match(/[0-9A-F]{2}/g)
var win1251arr = hexarr.map(el=> decodeMap[el]);
console.log(win1251arr.join(""))
// function below returns content of decodeMap from first snippet
function getHexToWin1251Dictionary(){
return {"10":"\u0010","11":"\u0011","12":"\u0012","13":"\u0013","14":"\u0014","15":"\u0015","16":"\u0016","17":"\u0017","18":"\u0018","19":"\u0019","20":" ","21":"!","22":"\"","23":"#","24":"$","25":"%","26":"&","27":"'","28":"(","29":")","30":"0","31":"1","32":"2","33":"3","34":"4","35":"5","36":"6","37":"7","38":"8","39":"9","40":"#","41":"A","42":"B","43":"C","44":"D","45":"E","46":"F","47":"G","48":"H","49":"I","50":"P","51":"Q","52":"R","53":"S","54":"T","55":"U","56":"V","57":"W","58":"X","59":"Y","60":"`","61":"a","62":"b","63":"c","64":"d","65":"e","66":"f","67":"g","68":"h","69":"i","70":"p","71":"q","72":"r","73":"s","74":"t","75":"u","76":"v","77":"w","78":"x","79":"y","80":"Ђ","81":"Ѓ","82":"‚","83":"ѓ","84":"„","85":"…","86":"†","87":"‡","88":"€","89":"‰","90":"ђ","91":"‘","92":"’","93":"“","94":"”","95":"•","96":"–","97":"—","98":"","99":"™","00":"\u0000","01":"\u0001","02":"\u0002","03":"\u0003","04":"\u0004","05":"\u0005","06":"\u0006","07":"\u0007","08":"\b","09":"\t","0A":"\n","0B":"\u000b","0C":"\f","0D":"\r","0E":"\u000e","0F":"\u000f","1A":"\u001a","1B":"\u001b","1C":"\u001c","1D":"\u001d","1E":"\u001e","1F":"\u001f","2A":"*","2B":"+","2C":",","2D":"-","2E":".","2F":"/","3A":":","3B":";","3C":"<","3D":"=","3E":">","3F":"?","4A":"J","4B":"K","4C":"L","4D":"M","4E":"N","4F":"O","5A":"Z","5B":"[","5C":"\\","5D":"]","5E":"^","5F":"_","6A":"j","6B":"k","6C":"l","6D":"m","6E":"n","6F":"o","7A":"z","7B":"{","7C":"|","7D":"}","7E":"~","7F":"","8A":"Љ","8B":"‹","8C":"Њ","8D":"Ќ","8E":"Ћ","8F":"Џ","9A":"љ","9B":"›","9C":"њ","9D":"ќ","9E":"ћ","9F":"џ","A0":" ","A1":"Ў","A2":"ў","A3":"Ј","A4":"¤","A5":"Ґ","A6":"¦","A7":"§","A8":"Ё","A9":"©","AA":"Є","AB":"«","AC":"¬","AD":"","AE":"®","AF":"Ї","B0":"°","B1":"±","B2":"І","B3":"і","B4":"ґ","B5":"µ","B6":"¶","B7":"·","B8":"ё","B9":"№","BA":"є","BB":"»","BC":"ј","BD":"Ѕ","BE":"ѕ","BF":"ї","C0":"А","C1":"Б","C2":"В","C3":"Г","C4":"Д","C5":"Е","C6":"Ж","C7":"З","C8":"И","C9":"Й","CA":"К","CB":"Л","CC":"М","CD":"Н","CE":"О","CF":"П","D0":"Р","D1":"С","D2":"Т","D3":"У","D4":"Ф","D5":"Х","D6":"Ц","D7":"Ч","D8":"Ш","D9":"Щ","DA":"Ъ","DB":"Ы","DC":"Ь","DD":"Э","DE":"Ю","DF":"Я","E0":"а","E1":"б","E2":"в","E3":"г","E4":"д","E5":"е","E6":"ж","E7":"з","E8":"и","E9":"й","EA":"к","EB":"л","EC":"м","ED":"н","EE":"о","EF":"п","F0":"р","F1":"с","F2":"т","F3":"у","F4":"ф","F5":"х","F6":"ц","F7":"ч","F8":"ш","F9":"щ","FA":"ъ","FB":"ы","FC":"ь","FD":"э","FE":"ю"};
}
I'm working with a system that integrates a Point of Sell (POS) device, I use chrome serial to scan ports and be able to read credit card data.
The problem I'm facing is that I need to concat the LRC from a string in this format:
STX = '\002' (2 HEX) (Start of text)
LLL = Length of data (doesn't include STX or ETX but command).
Command C50 {C = A message from PC to POS, 50 the actual code that "prints" a message on POS}
ETX = '\003' (3 HEX) (End of text)
LRC = Longitudinal Redundancy Check
A message example would be as follows:
'\002014C50HELLO WORLD\003'
Here we can see 002 as STX, 014 is the length from C50 to D, and 003 as ETX.
I found some algorithms in C# like this one or this one and even this one in Java, I even saw this question that was removed from SO on Google's cache, which actually asks the same as I but had no examples or answers.
I also made this Java algorithm:
private int calculateLRC(String str) {
int result = 0;
for (int i = 0; i < str.length(); i++) {
String char1 = str.substring(i, i + 1);
char[] char2 = char1.toCharArray();
int number = char2[0];
result = result ^ number;
}
return result;
}
and tried passing it to Javascript (where I have poor knowledge)
function calculateLRC2(str) {
var result = 0;
for (var i = 0; i < str.length; i++) {
var char1 = str.substring(i, i + 1);
//var char2[] = char1.join('');
var number = char1;
result = result ^ number;
}
return result.toString();
}
and after following the Wikipedia's pseudocode I tried doing this:
function calculateLRC(str) {
var buffer = convertStringToArrayBuffer(str);
var lrc;
for (var i = 0; i < str.length; i++) {
lrc = (lrc + buffer[i]) & 0xFF;
}
lrc = ((lrc ^ 0xFF) + 1) & 0xFF;
return lrc;
}
This is how I call the above method:
var finalMessage = '\002014C50HELLO WORLD\003'
var lrc = calculateLRC(finalMessage);
console.log('lrc: ' + lrc);
finalMessage = finalMessage.concat(lrc);
console.log('finalMessage: ' + finalMessage);
However after trying all these methods, I still can't send a message to POS correctly. I have 3 days now trying to fix this thing and can't do anything more unless I finish it.
Is there anyone that knows another way to calculate LRC or what am I doing wrong here? I need it to be with Javascritpt since POS comunicates with PC through NodeJS.
Oh btw the code from convertStringToArrayBuffer is on the chrome serial documentation which is this one:
var writeSerial=function(str) {
chrome.serial.send(connectionId, convertStringToArrayBuffer(str), onSend);
}
// Convert string to ArrayBuffer
var convertStringToArrayBuffer=function(str) {
var buf=new ArrayBuffer(str.length);
var bufView=new Uint8Array(buf);
for (var i=0; i<str.length; i++) {
bufView[i]=str.charCodeAt(i);
}
return buf;
}
Edit After testing I came with this algorithm which returns a 'z' (lower case) with the following input: \002007C50HOLA\003.
function calculateLRC (str) {
var bytes = [];
var lrc = 0;
for (var i = 0; i < str.length; i++) {
bytes.push(str.charCodeAt(i));
}
for (var i = 0; i < str.length; i++) {
lrc ^= bytes[i];
console.log('lrc: ' + lrc);
//console.log('lrcString: ' + String.fromCharCode(lrc));
}
console.log('bytes: ' + bytes);
return String.fromCharCode(lrc);
}
However with some longer inputs and specialy when trying to read card data, LRC becomes sometimes a Control Character which in my case that I use them on my String, might be a problem. Is there a way to force LRC to avoid those characters? Or maybe I'm doing it wrong and that's why I'm having those characters as output.
I solved LRC issue by calculating it with the following method, after reading #Jack A.'s answer and modifying it to this one:
function calculateLRC (str) {
var bytes = [];
var lrc = 0;
for (var i = 0; i < str.length; i++) {
bytes.push(str.charCodeAt(i));
}
for (var i = 0; i < str.length; i++) {
lrc ^= bytes[i];
}
return String.fromCharCode(lrc);
}
Explanation of what it does:
1st: it converts the string received to it's ASCII equivalent (charCodeAt()).
2nd: it calculates LRC by doing a XOR operation between last calculated LRC (0 on 1st iteration) and string's ASCII for each char.
3rd: it converts from ASCII to it's equivalent chat (fromCharCode()) and returns this char to main function (or whatever function called it).
Your pseudocode-based algorithm is using addition. For the XOR version, try this:
function calculateLRC(str) {
var buffer = convertStringToArrayBuffer(str);
var lrc = 0;
for (var i = 0; i < str.length; i++) {
lrc = (lrc ^ buffer[i]) & 0xFF;
}
return lrc;
}
I think your original attempt at the XOR version was failing because you needed to get the character code. The number variable still contained a string when you did result = result ^ number, so the results were probably not what you expected.
This is a SWAG since I don't have Node.JS installed at the moment so I can't verify it will work.
Another thing I would be concerned about is character encoding. JavaScript uses UTF-16 for text, so converting any non-ASCII characters to 8-bit bytes may give unexpected results.
I'm trying to read a binary file from the filesystem and then base64 encode it in JavaScript. I'm using the FileReader API to read the data and the base64 encoder found here.
The code I have seems close to working, the problem is that the generated base64 data is wrong. Here's what I've got so far:
function saveResource() {
var file = $(".resourceFile")[0].files[0];
var reader = new FileReader();
reader.onload = function(evt) {
var fileData = evt.target.result;
var bytes = new Uint8Array(fileData);
var binaryText = '';
for (var index = 0; index < bytes.byteLength; index++) {
binaryText += String.fromCharCode( bytes[index] );
}
console.log(Base64.encode(binaryText));
};
reader.readAsArrayBuffer(file);
};
Here's the file I'm testing with (it's a 100x100 blue square):
According to an online base64 decoder/encoder, this file should encode to:
/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxyiiiv3E8wKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z
...but instead what I'm getting out of the JavaScript is:
w7/DmMO/w6AAEEpGSUYAAQIAAAEAAQAAw7/DmwBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDLDv8ObAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMsO/w4AAEQgAZABkAwEiAAIRAQMRAcO/w4QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoLw7/DhADCtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDLCgcKRwqEII0LCscOBFVLDkcOwJDNicsKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5esKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDocOiw6PDpMOlw6bDp8Oow6nDqsOxw7LDs8O0w7XDtsO3w7jDucO6w7/DhAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgvDv8OEAMK1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIywoEIFELCkcKhwrHDgQkjM1LDsBVicsORChYkNMOhJcOxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXrCgsKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDosOjw6TDpcOmw6fDqMOpw6rDssOzw7TDtcO2w7fDuMO5w7rDv8OaAAwDAQACEQMRAD8Aw7HDiijCosK/cTzDgMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooA8O/w5k=
If i had to hazard a guess I'd say that the issue has something to do with nonprintable characters in the binary data (if I encode a plaintext document, that works fine). But what's the best approach to work around the issue?
Edit
It looks like this may be a problem with the base64 library itself (or if not that, then with how the Uint8Array is unpackaged into a string for the library call). If I use the browser's btoa() function instead, and pass it the Uint8Array binaryText directly, that works. Too bad that function doesn't exist in all browsers.
And Google to the rescue. I found the following code, which takes the input data as a plain array of "bytes" (numbers between 0 and 255, inclusive; also works fine if the Uint8Array is passed to it directly), and added it to the library I was using:
//note: it is assumed that the Base64 object has already been defined
//License: Apache 2.0
Base64.byteToCharMap_ = null;
Base64.charToByteMap_ = null;
Base64.byteToCharMapWebSafe_ = null;
Base64.charToByteMapWebSafe_ = null;
Base64.ENCODED_VALS_BASE =
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz' +
'0123456789';
/**
* Our default alphabet. Value 64 (=) is special; it means "nothing."
* #type {string}
*/
Base64.ENCODED_VALS = Base64.ENCODED_VALS_BASE + '+/=';
Base64.ENCODED_VALS_WEBSAFE = Base64.ENCODED_VALS_BASE + '-_.';
/**
* Base64-encode an array of bytes.
*
* #param {Array.<number>|Uint8Array} input An array of bytes (numbers with
* value in [0, 255]) to encode.
* #param {boolean=} opt_webSafe Boolean indicating we should use the
* alternative alphabet.
* #return {string} The base64 encoded string.
*/
Base64.encodeByteArray = function(input, opt_webSafe) {
Base64.init_();
var byteToCharMap = opt_webSafe ?
Base64.byteToCharMapWebSafe_ :
Base64.byteToCharMap_;
var output = [];
for (var i = 0; i < input.length; i += 3) {
var byte1 = input[i];
var haveByte2 = i + 1 < input.length;
var byte2 = haveByte2 ? input[i + 1] : 0;
var haveByte3 = i + 2 < input.length;
var byte3 = haveByte3 ? input[i + 2] : 0;
var outByte1 = byte1 >> 2;
var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
var outByte4 = byte3 & 0x3F;
if (!haveByte3) {
outByte4 = 64;
if (!haveByte2) {
outByte3 = 64;
}
}
output.push(byteToCharMap[outByte1],
byteToCharMap[outByte2],
byteToCharMap[outByte3],
byteToCharMap[outByte4]);
}
return output.join('');
};
/**
* Lazy static initialization function. Called before
* accessing any of the static map variables.
* #private
*/
Base64.init_ = function() {
if (!Base64.byteToCharMap_) {
Base64.byteToCharMap_ = {};
Base64.charToByteMap_ = {};
Base64.byteToCharMapWebSafe_ = {};
Base64.charToByteMapWebSafe_ = {};
// We want quick mappings back and forth, so we precompute two maps.
for (var i = 0; i < Base64.ENCODED_VALS.length; i++) {
Base64.byteToCharMap_[i] =
Base64.ENCODED_VALS.charAt(i);
Base64.charToByteMap_[Base64.byteToCharMap_[i]] = i;
Base64.byteToCharMapWebSafe_[i] =
Base64.ENCODED_VALS_WEBSAFE.charAt(i);
Base64.charToByteMapWebSafe_[
Base64.byteToCharMapWebSafe_[i]] = i;
}
}
};
The full code for the library containing the above functions is available here, but in its non-modified form it appears to depend upon a number of other libraries. The slightly hacked-up version above should work for anyone who just needs a quick fix for this issue.
Treat the binary as arraybuffer, this is independent from any character encoding. Your blue-square (.jpg) has 361 native bytes, means octets from 0..255 (decimal) and they are no characters!
It means: Use ArrayBuffer for encoding this to Base64 with well known base64-algorithm.
With Perl back to origin, shows the blue-square as above:
my $fh = IO::File->new;
$fh->open("d:/tmp/x.jpg", O_BINARY|O_CREAT|O_RDWR|O_TRUNC) or die $!;
$fh->print(decode_base64("/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBD
AQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAFQABAQAA
AAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAUH/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMR
AD8AjgDcUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//2Q==
"));
$fh->close;