忙里偷闲把最近遇到的一些问题总结一下 ^^
golang 序列化 json 时自动转换特殊字符
在使用 json.Marshal 时,会自动对其中的特殊字符进行转换,例如会将 & 替换为 \u0026
。一般情况下应该问题不大,但是需要知道它在这儿把数据内容做了转化。另外,golang 还会自动将 \b
转为 \u0008
,也需要注意一下。
1 | package main |
golang 和 python 在序列化 array 时的差异
golang 序列化数组时,数据各 item 之间不会添加空格,但是默认情况下 python 会。一般情况下不会造成啥影响,但是需要知道有这个差异。
1 | package main |
1 | #! /usr/bin/env python |
对于 separators 参数的解释,gpt 的解释如下
用于指定序列化时元素之间以及键值对之间的分隔符。这个参数接受一个包含两个元素的元组,第一个元素是用于分隔字典中的键值对的字符串,第二个元素是用于分隔列表或者字典中的元素的字符串。默认值为
(', ', ': ')
,这意味着字典中的键值对之间以及列表或者字典中的元素之间都会有一个空格
错误使用 awk 导致数据内容被误改
数据样例如下,按照空格分割,需要把第一列删除。注意,后面列中存在多个空格
1 | url1 {"key":"value 123"} |
按照 gpt 的回答,使用如果 awk 命令处理
1 | awk '{ $1=""; sub(/^ /, ""); print $0 }' file.txt |
但是结果中,后面列中多余的空格被删掉了
1 | {"key":"value 123"} |
可以使用如下 awk 命令
1 | awk '{sub($1 FS, ""); print}' file.txt |
gpt 对这个命令的解释如下
sub($1 FS, "")
$1
表示第一个字段FS
是字段分隔符$1 FS
组合起来就是第一个字段加上紧随其后的制表符sub()
函数将$1 FS
替换为空字符串""
,effectively 删除它们
print
: 打印处理后的行
但其实最保险的方式还是写个 python 脚本来处理,或者用 sed 命令
1 | sed 's/^[^ ]* //' file.txt |
将 map 作为 bash function 的参数
去年年末在写一个 bash 脚本的时候,有一个需求是将 map 作为 function 的入参,实现起来比较麻烦,记录一下。bash 的版本为 GNU bash(bdsh), version 4.1.17(2)-release (x86_64-unknown-linux-gnu)
实现代码
ref: https://stackoverflow.com/questions/31307210/what-does-1-mean-in-bash
1 | set -eux |
参数解析流程分析
1 | input_map_raw="$(declare -p ${1})" |
declare -p 的作用是 “print the attributes and options of the variables” ,ref https://linuxhint.com/bash_declare_command/。作用可以理解为将参数的定义展开,感觉类似于 sql 的 show create table,这里的结果是
1 | declare -A weapons='(["Imperial Sword"]="90" ["Tainted Dagger"]="54" ["Edged Shuriken"]="25" ["Straight Sword"]="75" )' |
现在 input_map_raw 变量的值就是上面的表达式。随后第二行, eval 命令会执行一遍其后的命令,将一些变量进行替换,生成第二次执行的命令,随后再次执行,ref https://www.cnblogs.com/itcomputer/p/5035387.html,在这里,${input_map_raw#*=} 是一个删除表达式,格式是 ${xxx#pattern},会根据 pattern 删除 xxx 中的值,ref https://stackoverflow.com/questions/31307210/what-does-1-mean-in-bash ,这里是根据 *= 进行匹配,匹配到的值是 declare -A weapons=
,然后将其删除
1 | # eval 生成的命令 |
此时 arg_array 的值和外部传入的 weapons 保持一致了。不得不说,太 trick 了,希望后面可以加一些语法糖
python2 & python3 字符串编码差异
知识介绍
https://reata.github.io/blog/how-python-does-unicode/
python2:
str 和 unicode 两种类型
str 实际上是 “字节串”(byte string/sequence of bytes),某种特定编码的字节序列,默认为 ascii
unicode 表示是 “unicode字符”
str 创建方式:str() 或 “xxx”
unicode 创建方式:unicode() 或 u”xxxxx”
python3
一种字符串类型:str,是 unicode
字节串用 bytes 数据结构表示
何为 unicode
不是一种编码,和 ascii、gbk、utf-8 不是一种类型
本质上是一种字符集,每个字符对应一个唯一 id
基本单元:码位(code point),一个字符对应一个或者多个码位,或一个码位能对应多个字符
unicode 是字符集(码位集)
规定了一个码位的二进制代码
没有规定这个二进制代码该如何存储
总结:
字节流 – [ decode ] –> unicode
字节流 <– [ encode ] – unicode
对于 py2,默认的 str 为字节流,需要转为 unicode 需要用 unicode() 包一下
对于 py3,默认的 str 为 unicode,字节流为 bytes
代码样例
样例1 (gb18030 编码文件转 utf8)
1 | # python2 |
1 | # python3 |
样例2:(读取一个 gb18030 编码的json:{“a”:”運動”})
1 | # python2 |
1 | # python3 |
从上可以看出,json.loads 生成的 json 字段默认为 unicode
c++ map emplace & insert 注意事项
这个问题是从内部的一个 case study 里了解到的,这个错误用法引起了比较严重的线上事故。
使用 insert 或 emplace 不会更新已有 key 的值,需要使用 m[k]=v
来更新,代码样例如下:
1 |
|
gpt 的解释是:
std::map
的insert
和emplace
方法设计的目的是保持 map 中的键的唯一性。当你试图插入一个已经存在的键时,insert
和emplace
方法不会替换现有的键值对,而是简单地忽略你的插入请求。这是因为std::map
是一个关联容器,它通过键进行查找和访问值,所以需要保证键的唯一性。