1

我需要一个 JS 编码器到 base91 或 Ascii85 来获取二进制数。我确实有一个谷歌表,如下所示: 在此处输入图像描述

代码是:

function columnToLetter(column)
{
  //https://stackoverflow.com/a/21231012/14226613
  var temp, letter = '';
  while (column > 0)
  {
    temp = (column - 1) % 26;
    letter = String.fromCharCode(temp + 65) + letter;
    column = (column - temp - 1) / 26;
  }
  return letter;
}

function onEdit(e){
  var activeSheet = e.source.getActiveSheet();
  if (activeSheet.getActiveCell().getRow() == 3) {
    var spreadsheet = SpreadsheetApp.getActive();
    var str = ''
    for (i=0; i < 32; i++) {
      var colName = columnToLetter(i+1)
      if (spreadsheet.getRange(colName+ '3').isChecked()) {
        str = str + '1'
      }  else {
        str = str + '0'
      }
    }
    let number = parseInt(str,2)
    let hexx = ((+number).toString(16))
    spreadsheet.getRange('R7').setValue(hexx);
  }
}

目前对十六进制的编码效果很好——但我需要一种更有效的方法来编码这个二进制标志。

目的:这种编码模式将来可能是产品/备件名称的一部分,其中我最多有 5-6 个字符(就像 80 年代的 :D 一样)。

Ascii85 会很棒,因此'ffffffff' 的表示是' s8W-!' 我会保存 3 个字符。对于测试/编码,我使用了 cryptii

解决方案应该是没有外部依赖/要求的纯 JS 和/或应该能够在 Google 的环境中运行。您知道我可以为此目的使用的任何库吗?Base91 也可以——只要我们有可打印的字符。完美的解决方案将是可配置的 JS 编码器/解码器 - 可以预先选择用于编码的模式和字符。

更新:

发现 Ascii85 或 Base91 或不适合宣布每部手机的代码 - 因此您不想在键盘上轻松找到所有字符。确实,base64 效率较低,但通过调整要求,我能够找到最大的解决方案。实验几天后4-5个字符。我将尝试回答我自己的问题 - 见下文。更新要求:

  • 16 位有效载荷
  • 4 位(编号 1..15)用于家庭/配方/类型选择
  • CRC4 4 位
  • base64 编码,没有外部依赖和可调整的字母表
4

2 回答 2

0

这个解决方案对我有用。整个 3Byte 被编码成 4 个字符。我调整了字母表来替换一些误导性字符(0、O、i、l、1 等)。base64 填充('=')在编码后被删除,并将在函数内部解码之前添加。CRC4 并不完美 - 总比没有 CRC 好 :)

我很高兴收到任何反馈,建议进一步优化。谢谢。

谷歌表格前端: 在此处输入图像描述

本文档中的另一个选项卡,您可以在其中定义变体/系列/食谱: 在此处输入图像描述

下面是应用程序脚本的代码(特别感谢 @Kamil Kiełczewski 的 base64 snippert):

// Convert a hex string to a byte array
function hexToBytes(hex) {
    // https://stackoverflow.com/a/34356351/14226613
    for (var bytes = [], c = 0; c < hex.length; c += 2)
    bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}

function hexToBytes2(hexString) {
  //https://stackoverflow.com/a/62365404/14226613
  return hexString.match(/.{1,2}/g).map(x=> +('0x'+x));
}

// Convert a byte array to a hex string
function bytesToHex(bytes) {
    // https://stackoverflow.com/a/34356351/14226613
    for (var hex = [], i = 0; i < bytes.length; i++) {
        var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
        hex.push((current >>> 4).toString(16));
        hex.push((current & 0xF).toString(16));
    }
    return hex.join("");
}

function hex2bin(hex, fill = false){
  //https://stackoverflow.com/a/45054052/14226613
  let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0'); 
  if (fill) {
    //https://stackoverflow.com/a/27641835/14226613
    bin = "000000000000000000000000".substr(bin.length) + bin;
  }
  return bin
}


function b64(){
  const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
  function encode (byteArray) {
    //https://stackoverflow.com/a/62365404/14226613
    const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
    const l = byteArray.length
    let result = '';
    for(let i=0; i<=(l-1)/3; i++) {
      let c1 = i*3+1>=l; // case when "=" is on end
      let c2 = i*3+2>=l; // case when "=" is on end
      let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
      let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));  
      result += r.join('');
    }
    //remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
    result = result.replace(/={1,2}$/, '');
    return result;
  }

  function decode (str) {
    //https://stackoverflow.com/a/62364519/14226613
      let result = [];
      //add padding http://jsfiddle.net/bicycle/c49fgz8x/
      str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
      for(let i=0; i<str.length/4; i++) {
        let chunk = [...str.slice(4*i,4*i+4)]
        let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join(''); 
        let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
        result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
      }
      return result;
  }
  
  return {
    encode: encode,
    decode: decode
  }
}


var crc4 = function(data) {
    //https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
    var POLY = 0xC, INIT = 0, XOROUT = 0;
    for(var crc = INIT, i = 0; i < data.length; i++) {
        crc = crc ^ data[i];
        for(var j = 0; j < 8; j++) {
            crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
        }
    }
    return (crc ^ XOROUT) & 0xF;
};

function get_family(input, sheet, range) {
  //get array with family names
  let familiesValNames =sheet.getRange(range).getValues();
  let obj = {};
  for (i=0; i < familiesValNames.length; i++) {
    if (  //if input is string - loop through array and find the corresponding number
          (typeof input === 'string' && input == familiesValNames[i]) ||
          //if it is a number between 1..15
          (typeof input === 'number' && input == (i+1))) {      
            obj.number = i + 1;
            //first values are in row 4 (=offset)
            let sRange = 'A' + (4 + i) + ':P' + (4 + i)
            obj.names = sheet.getRange(sRange).getValues();
            break;
    }
  }
  return obj
}

function set_family_object (sheet, range, fixed = false){
  let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
  
  if (fixed) {
    return famtypes
  } else {
    let familiesValNames =sheet.getRange(range).getValues();
    let sRange
    let obj = {};
    for (i=0; i < 15; i++) {
      sRange = 'A' + (4 + i) + ':P' + (4 + i)
      Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
      obj[i +1].types = sheet.getRange(sRange).getValues();
    }
  }
  
  return JSON.stringify(obj);
}

function encoder (sheet, currentRow, colOffset, typeColNo, famSheet, famSheetTypesRange) {
  let ret = {}
  var str = ''
  //loop through columns 3 to 18 of the corresponding row and iterate if its checked -> result binary string
  for (i=0; i < 16; i++) {
    str = (sheet.getRange(currentRow, (i+colOffset)).isChecked()) ? str + '1' : str + '0';
  }
  //shift one byte to left for easier calculation
  //result are the first 2 byte (0xnnnnFF) of 3 (0xFFFFFF)
  let var_bin = ((+parseInt(str,2)) << 8).toString(2)
  //format binary as 3 byte 
  var_bin = ("000000000000000000000000".substr(var_bin.length) + var_bin);
  //convert to hex
  ret.variant_hex = parseInt(var_bin, 2).toString(16)

  //get selected family type
  ret.family_str = sheet.getRange(currentRow, typeColNo).getValue()
  //get number of this selected type and convert it to hex
  ret.family_hex = (get_family(ret.family_str, famSheet, famSheetTypesRange).number >>> 0).toString(16).toUpperCase();

  //calculate intermedia hex value; add a trailing '0' to move the family to the upper 4bits of the last byte
  ret.joined_hex = (parseInt(ret.variant_hex, 16) + parseInt((ret.family_hex + '0'), 16)).toString(16)

  //convert to  binary - better to make CRC with binary (1111 1111) than with HEX (ff)
  let joined_bin = (parseInt(ret.joined_hex, 16)).toString(2)
  //get crc and convert it to hex
  ret.crc_hex = crc4(joined_bin).toString(16).toUpperCase()
  
  //final hex code
  ret.final_hex = (parseInt(ret.joined_hex, 16) + parseInt(ret.crc_hex, 16)).toString(16).toLocaleUpperCase()

  //prepare and encode into base64
  /** @type {number[]} */
  ret.finalByteArr = [];
  ret.finalByteArr[0] = ((parseInt(ret.final_hex, 16) & (parseInt('ff0000', 16))) >>> 16)
  ret.finalByteArr[1] = ((parseInt(ret.final_hex, 16) & (parseInt('00ff00', 16))) >>> 8)
  ret.finalByteArr[2] = (parseInt(ret.final_hex, 16) & (parseInt('0000ff', 16)))
  //ret.finalByteArr = hexToBytes(ret.final_hex)
  ret.b64 = b64().encode(ret.finalByteArr)
  return ret
}

function decoder(str) {
  var ret = {}
  //will find on the end of the string the code; trailing whitespaces will be ignored
  const regEx = /^(?:.*)#(.{4})(?:\s*)$/
  //check if string is valid (checksum will be checked later)
  ret.strValid = regEx.test(str);
  if (ret.strValid) {
    let result = str.match(regEx)
    //first capturing group
    ret.b64 = result[1];
    //convert to a byte array
    ret.byteArray = b64().decode(ret.b64)
    //convert to hex string
    ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
    //convert the hex string to binary
    ret.binary = hex2bin(ret.hex, true)
    //get crc
        ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
    //get last 2 bytes (mask the 1st out) and check if its suits to the checksum
    let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
    ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
    //get only variant
    ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
    
    //fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
    let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
    ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
    //convert to hex
    ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
        //get type - mask everythin irrelevant out and move 4 bits to right
    ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
  } 
  else {
    ret.strValid = false;
  }
  return ret
}

function onEdit(event){
  var sheet = event.source.getActiveSheet();
  var actualSheetName = sheet.getName();
  var lastColumnRow = sheet.getLastColumn();
  var editRange = sheet.getActiveRange();
  var editCol = editRange.getColumn();
  var editRow = editRange.getRow();
  var value = sheet.getActiveCell().getValue();
  var adress = sheet.getActiveCell().getA1Notation();
  var familiesSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Families');

  //evaluate if something has been changed in 'Familees'
  if (actualSheetName == 'Families') {
    let all_types = set_family_object(familiesSheet, 'Q4:Q18', true)
    Logger.log(all_types)
    Logger.log(all_types[1].family)
    Logger.log(all_types[1].types)
    Logger.log(all_types[15].family)
    Logger.log(all_types[15].types)
  }

  //evaluate and fill out legend on the top of sheet
  if (adress == 'S2' && actualSheetName == 'Spares') {
    const str = sheet.getActiveCell().getValue();
    var famObj = get_family(str, familiesSheet, 'Q4:Q18')
    sheet.getRange('C2:R2').setValues(famObj.names)
  }
  
  //getRange(row, column, [numRows], [numColumns]) || getRange(a1Notation)
  if (sheet.getActiveCell().getRow() >= 6 && sheet.getActiveCell().getRow() <= 21 
      && sheet.getActiveCell().getColumn() >= 3 && sheet.getActiveCell().getColumn() <= 19 
      && actualSheetName == 'Spares') {
        let currentRow = sheet.getActiveCell().getRow();
        let currentCol = sheet.getActiveCell().getColumn();

        //calculate code
        let codeEnc = encoder(sheet, currentRow, 3, 19, familiesSheet, 'Q4:Q18')
        if (sheet.getRange(currentRow, 1).getValue().length > 3) {
          sheet.getRange(currentRow, 20).setValue(codeEnc.crc_hex);
          sheet.getRange(currentRow, 20).setComment('')
          sheet.getRange(currentRow, 21).setValue(codeEnc.b64);  
          sheet.getRange(currentRow, 21).setComment('')
        } else {
          sheet.getRange(currentRow, 20).setValue('n/a');
          sheet.getRange(currentRow, 20).setComment('Please enter a product name in the 1st column.')
          sheet.getRange(currentRow, 21).setValue('n/a');
          sheet.getRange(currentRow, 21).setComment('Please enter a product name in the 1st column.')
        }

        let decoded = decoder('grgrgr #' + codeEnc.b64)
        Logger.log(codeEnc)
        Logger.log(decoded)
        var famObj2 = get_family(decoded.type, familiesSheet, 'Q4:Q18')
        Logger.log(famObj2)
  }

可以使用以下 HTML/JS 片段尝试/解码结果。它又快又脏。只需输入例如:

  • 气动接头#Xs2a
  • 相机#!$cg
  • 电源 24V #p%qz

您可以尝试手动更改 base64 代码 - 以测试 CRC4 是否有效。

var crc4 = function(data) {
        //https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
        var POLY = 0xC, INIT = 0, XOROUT = 0;
        for(var crc = INIT, i = 0; i < data.length; i++) {
            crc = crc ^ data[i];
            for(var j = 0; j < 8; j++) {
                crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
            }
        }
        return (crc ^ XOROUT) & 0xF;
        };


        // Convert a hex string to a byte array
        function hexToBytes(hex) {
            // https://stackoverflow.com/a/34356351/14226613
            for (var bytes = [], c = 0; c < hex.length; c += 2)
            bytes.push(parseInt(hex.substr(c, 2), 16));
            return bytes;
        }

        // Convert a byte array to a hex string
        function bytesToHex(bytes) {
            // https://stackoverflow.com/a/34356351/14226613
            for (var hex = [], i = 0; i < bytes.length; i++) {
                var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
                hex.push((current >>> 4).toString(16));
                hex.push((current & 0xF).toString(16));
            }
            return hex.join("");
        }

        function hex2bin(hex, fill = false){
          //https://stackoverflow.com/a/45054052/14226613
          let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0'); 
          if (fill) {
            //https://stackoverflow.com/a/27641835/14226613
            bin = "000000000000000000000000".substr(bin.length) + bin;
          }
          return bin
         }


        function b64(){
          const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
          function encode (byteArray) {
            //https://stackoverflow.com/a/62365404/14226613
            const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
            const l = byteArray.length
            let result = '';
            for(let i=0; i<=(l-1)/3; i++) {
              let c1 = i*3+1>=l; // case when "=" is on end
              let c2 = i*3+2>=l; // case when "=" is on end
              let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
              let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));  
              result += r.join('');
            }
            //remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
            result = result.replace(/={1,2}$/, '');
            return result;
          }

          function decode (str) {
            //https://stackoverflow.com/a/62364519/14226613
              let result = [];
              //add padding http://jsfiddle.net/bicycle/c49fgz8x/
              str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
              for(let i=0; i<str.length/4; i++) {
                let chunk = [...str.slice(4*i,4*i+4)]
                let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join(''); 
                let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
                result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
              }
              return result;
          }
          
          return {
            encode: encode,
            decode: decode
          }
        }

        function set_family_object (sheet, range, fixed = false){
          let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
          
          if (fixed) {
            return famtypes
          } else {
            let familiesValNames =sheet.getRange(range).getValues();
            let sRange
            let obj = {};
            for (i=0; i < 15; i++) {
              sRange = 'A' + (4 + i) + ':P' + (4 + i)
              Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
              obj[i +1].types = sheet.getRange(sRange).getValues();
            }
          }
          
          return JSON.stringify(obj);
        }

        function decoder(str) {
          var ret = {}
          //will find on the end of the string the code; trailing whitespaces will be ignored
          const regEx = /^(?:.*)#(.{4})(?:\s*)$/
          //check if string is valid (checksum will be checked later)
          ret.strValid = regEx.test(str);
          if (ret.strValid) {
            let result = str.match(regEx)
            //first capturing group
            ret.b64 = result[1];
            //convert to a byte array
            ret.byteArray = b64().decode(ret.b64)
            //convert to hex string
            ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
            //convert the hex string to binary
            ret.binary = hex2bin(ret.hex, true)
            //get crc
                ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
            //get last 2 bytes (mask the 1st out) and check if its suits to the checksum
            let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
            ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
            //get only variant
            ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
            
            //fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
            let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
            ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
            //convert to hex
            ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
                //get type - mask everythin irrelevant out and move 4 bits to right
            ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
          } 
          else {
            ret.strValid = false;
          }
          return ret
        }

        document.getElementById('ssearch').onclick = function() {
          let ttable = document.querySelector("body > table")
          ttable.style.visibility  = "visible";
          
          let sval = document.getElementsByClassName('searchTerm')[0].value;
            let dec_val = decoder(sval);
          let famValues = set_family_object(null, null, true)
          //console.log(dec_val)
          //console.log(famValues)
          
          var tbody = document.querySelector('tbody');
          tbody.innerHTML = '';
          if (!dec_val.strValid) {
            tbody.innerHTML += '<tr style="background-color:red;color:white;"><td>' + 'String Validation' + '</td><td>Input String is not valid!</td></tr>'
          } else {
            if (dec_val.crcValid) {
                tbody.innerHTML += '<tr style="background-color:#93C47D; font-size: 75%;"><td>' + 'String & CRC' + '</td><td>String & CRC valid</td></tr>'
              tbody.innerHTML += '<tr style="background-color:#3C6478; color:white"><td>' + 'Family' + '</td><td>' + famValues[dec_val.type]['family'] + '</td></tr>'
              for (var i = 0; i < 16; i++) {
                     tbody.innerHTML += '<tr><td>' +
                  [
                      ((dec_val.variant_bin_mirr[i] == 1) ? 
                        famValues[dec_val.type]['types'][0][15-i] :
                        '<s>' + famValues[dec_val.type]['types'][0][15-i] + '</s>'),
                      ((dec_val.variant_bin_mirr[i] == 1) ? 
                        '<span style="background-color:#93C47D; color:white">Compatible</span>' : 
                        '<span style="background-color:red; color:white">NOT Compatible</span>')
                  ].join('</td><td>') + '</td></tr>'
                }
            } else{
              tbody.innerHTML += '<tr style="background-color:red;color: white;"><td>' + 'CRC' + '</td><td>Product Name is wrong - CRC invalid</td></tr>'
            }
          }

        }
th, td {
            border: 1px solid black;
        }
        table {
          margin: auto;
          width: 40%;
          visibility: hidden;
        }

        @import url(https://fonts.googleapis.com/css?family=Open+Sans);

        body{
          background: #f2f2f2;
          font-family: 'Open Sans', sans-serif;
        }

        .search {
          width: 100%;
          position: relative;
          display: flex;
        }

        .searchTerm {
          width: 100%;
          border: 3px solid #00B4CC;
          border-right: none;
          padding: 5px;
          height: 20px;
          border-radius: 5px 0 0 5px;
          outline: none;
          color: #9DBFAF;
        }

        .searchTerm:focus{
          color: #00B4CC;
        }

        .searchButton {
          width: 100px;
          height: 36px;
          border: 1px solid #00B4CC;
          background: #00B4CC;
          text-align: center;
          color: #fff;
          border-radius: 0 5px 5px 0;
          cursor: pointer;
          font-size: 20px;
        }

        /*Resize the wrap to see the search bar change!*/
        .wrap{
          width: 50%;
          position: absolute;
          top: 10%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
<!-- https://codepen.io/huange/pen/rbqsD -->
    <!-- https://uicookies.com/html-search-box/ -->
    <div class="wrap">
       <div class="search">
          <input type="text" class="searchTerm" placeholder="Give me your product name...">
          <button type="submit" class="searchButton" id="ssearch">
            Check
         </button>
       </div>
    </div>

    <br><br><br><br><br><br><br><br>

    <table>
        <thead>
            <th style="width:40%;text-align:left;">Name</th>
            <th style="text-align:left;">Value</th>
        </thead>
        <tbody>
        </tbody>
    </table>

于 2021-02-08T06:14:03.350 回答
0
function columnToLetter(col) {
  const ss=SpreadsheetApp.getActive();
  const sh=ss.getSheetByName('Sheet1');
  return sh.getRange(1,col).getA1Notation().slice(0,-1);
}

由于您要多次使用它来创建一个对象,其中包含您需要的所有列作为属性,并且它将为您创建一个非常快速的转换器,无需函数调用和任何上下文切换时间。

于 2021-02-03T21:49:42.627 回答