Posts Tagged ‘Code杂谈’

字符串乱码解决之道

January 5th, 2017

最近工作压力太大,blog也逐渐荒废,怎么也得写点东西了,要不说不过去。。。。。

Qt为字节流和字符串分别提供了QByteArray和QString两个类(还有QLatin1String等其他类,但这两个是最主要的)。当我们涉及到I/O时,比如读写文件、读写网络socket、控制台输入输出、读写串口… 操作的都是字节流,如果我们此时需要操作的内容是字符串,则需要二者之间的相互转换。在C和C++中,我们一般都是将 “Hello World!” 这种称为字符串。但是就目前而言,当我们提字符串时,一般是指一个Unicode字符串,其由一个一个的Unicode字符构成;当我们提字节流时,是指一个一个的字节。或许我们可以说,ANSI C/C++截止目前只有字节流,而缺乏对字符串的支持。另外各个编译器对编码的支持又严重不一, Qt为解决这个问题提供了QTextCodec。

QTextCodec * textc = QTextCodec::codecForName("GBK");
1.QTextCodec::setCodecForCStrings(textc);
2.QTextCodec::setCodecForTr(textc); 
3.QTextCodec::setCodecForLocale(textc);

QString 是不存在中文支持问题的,很多人遇到问题,并不是本身 QString 的问题,而是没有将自己希望的字符串正确赋给QString。很简单的问题,”我是中文”这样写的时候,它是传统的char 类型的窄字符串,我们需要的只不过是通过某种方式告诉QString 这四个汉字采用的那种编码。而问题一般都出在很多用户对自己当前的编码没太多概念。另外文件是有编码的,但是这种纯文本文件却不会记录自己采用的编码,这个是问题的根源。真的是 QString 乱码了吗?其实很简单的一个问题,当你从窄字符串 char* 转成Unicode的QString字符串的时候,你需要告诉QString你的这串char* 中究竟用的是什么编码?GBK、BIG5还是Latin-1。理想情况就是:将char* 传给QString时,同时告诉QString自己的编码是什么;但是QString 提供的成员函数,远远满足不了大家的需求,于是只有采取语句1的办法。

tr(“中文”);与QString(“中文”);一样,你必须告诉tr这个窄字符串是何种编码?你不告诉它,它就用latin1。于是所谓的乱码问题就出来了。如何告诉tr你写的这几个汉字在磁盘中保存的是何种编码呢?这正是语句2所做的。如果你的编码采用的utf8,可以直接使用trUtf8而不必设置setCodecForTr()。

对于语句3应该没什么好说的,在绝大多数情况下,我们在代码中应该都用不到这个函数(默认的system应该比我们所能设置的要好)。
下面讲一下关于编码转换问题:
QT中的QString内容使用Unicode作为文本编码。但是实际系统中通常采用的是其他编码,例如GBK,utf8等。为了便于兼容这些格式,QT中还设置了两个字符串类型:
QCString类: C类型字符串,必须以\0结尾,也就是中间不能含有\0. 例如GBK编码的字符串
QByteArray类: 中间可以含有\0.例如utf8编码的字符串

在设置下面的代码基础上:

QTextCodec *gbk = QTextCodec::codecForName("gb18030"); 
QTextCodec *utg8 = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForTr(gbk);
QTextCodec::setCodecForLocale(gbk);
QTextCodec::setCodecForCStrings(gbk);

1. UTF-8 转换 GBK

QString U2G(QString utfStr)
{
   return gbk->toUnicode(utfStr.toLocal8Bit());
}

2 GBK 转换 UTF-8

QString U2G(QString gbkStr)
{
   return utg8->toUnicode(gbkStr.toUtf8());
}

代码示例:
———————————————————————————–

 QTextCodec *gbk = QTextCodec::codecForName("gb18030");
 QTextCodec *utf8 = QTextCodec::codecForName("utf-8");

 QTextCodec::setCodecForTr(gbk);
 QTextCodec::setCodecForLocale(gbk);
 QTextCodec::setCodecForCStrings(gbk);


 QFile file("../test.txt");
 file.open(QIODevice::ReadOnly);
 QByteArray readByte = file.readAll();
 QString readStr = utf8->toUnicode(readByte.data());
 file.close();
 QString utfStr = QObject::trUtf8(readByte); //utf-8
 QString gbkStr = QObject::tr("中文"); // gbk

 QString utf2gbk = gbk->toUnicode(readStr.toLocal8Bit()); // utf8 conver gbk
 QString gbk2utf1 = utf8->toUnicode(utf2gbk.toUtf8()); // gbk convert utf8
 QString g2u = gbk->toUnicode(gbk->fromUnicode(readStr)); // gbk convert utf8

 qDebug() << "gbk:" << gbkStr;
 qDebug() << "utf8:" << utfStr;
 qDebug() << "readStr:" << readStr;

 qDebug() << "read_size:" << readByte.length();
 qDebug() << "utf2gbk:" <<utf2gbk << "length:" << readStr.toLocal8Bit().length();
 qDebug() << "gbk2utf8-1:" << gbk2utf1 << " length: " << utf2gbk.toUtf8().length();
 qDebug() << "g2u" << g2u << "length:" << gbk->fromUnicode(utfStr).length();

 QLabel *label = new QLabel(utf2gbk);
 label->show();

参考:

【1】http://www.cnblogs.com/bingcaihuang/archive/2011/03/17/1986714.html

【2】http://knight4576.blog.51cto.com/2761974/703963

使用NSIS工具制作安装包

November 21st, 2016

发布自己编写的程序是一件令人兴奋的事,在windows平台有InstallSheild和NSIS工具。

下面我使用NSIS来打包我的程序,对于打包不熟悉的人,该工具提供了向导方式,我们可以一步一步打包自己的程序。NSIS 大体布局如下:

  • 预设参数(包括外部压缩器选择、编译选项、宏定义以及文件包含等)
  • 普通安装设置
  • 自定义函数
  • 安装程序区域内容
  • 安装程序回调函数及其相关函数定义
  • 卸载程序区域内容
  • 卸载程序回调函数及其相关函数定义

如果对打包程序要求不太高的话,我们的工作主要集中安装程序区域内容和卸载程序区域内容,其他部分我们均可以使用向导默认生成,【1】中讲的比较细致,我们可以按照教程一步一步操作。在要选择要打包的文件时,直接把要打包的目录内容全部添加进来,注意两处*.*,以及勾选“包含子目录”,不要勾选”单独添加每个文件“,编译的时候会把所有的文件打包进来的。

281552003955288

我之前就是没有加*.*导致只加了工作目录下的文件,而那些子目录完全没有打包进去。之后就是设置一些快捷方式什么的,在结束向导的时候,我们需要勾选”保存脚本“,以及”转换文件路径到相对路径“,这样可以方便下次打包使用。

281552197992145

以上是通过图形化方式打包生成脚本,最后按Ctrl+F9编译成安装包。而通过编辑脚本的方式就是改脚本代码啦,虽然这种方式比较晦涩,但是可以更清楚展示软件包安装过程。【2】是一个实例脚本,我们只需要关注这两条语法:

; 循环包含目录下全部内容
File /r "F:\12\1\*.*"
; 只包含一个文件
File "F:\12\gf.gif"

Delete "$TEMP\magiclime.exe"
RmDir /r "SMPROGRAMSNSIS"

加入/r 就是循环加入,而不加/r 就是只添加该文件。RmDir主要是删除该子文件夹下的所有文件和子文件夹,而Delete是删除该文件  ,/r就是循环删除。

 

参考:

【1】http://www.cnblogs.com/modou/p/3573772.html

【2】示例脚本

SSH 使用 — 如何从外网连接内网计算机

June 11th, 2016

SSH是我们经常使用的工具,但是因为现在组网方式非常复杂,导致跨越NAT非常难因应对,同一局域网下的SSH使用,我这里就不再赘述。我们来聊聊如何从外网连接内网的ssh-server。

首先我们可以选择偷懒的方式,设置主机别名,这样就不用每次都输入ssh user@remote -p port 了,我们可以在~/.ssh/config 里面追加以下内容:

Host lab
   HostName xxx.xxx.xxx.xxx
   User user
   Port port

保存之后,即可用 ssh lab 登入,如果还配置了公钥登入,那就连密码都不用输入了。

================

两台主机之间传输数据可以使用scp命令,它与ssh -p port 很类似,稍微的差别在与指定端口时用的是大写的 -P 而不是小写的。有了这些基础知识,下面我们聊聊如何从外网连接内网的ssh-server。为了实现这个功能,我们必须拥有一台公网主机,现在国内的vps非常便宜,阿里云或者腾讯云都可以。

假设现在我有一台处在公网的机器 bridge,这台机器拥有公网IP 。你在实验室也有一台机子lab,这台机子只能在实验室内部访问,但他可以访问公网,你希望能在任何地方都能访问这台机器。那么使用 ssh -R 可以轻松地做到这个事情。

lab$ ssh -R 10022:localhost:22 bridge
bridge$ ssh user@localhost -p 10022
lab$

只要上面这个过程成功了,就说明在你执行 ssh -R 10022:localhost:22 bridge(ssh -R 远端:本地 bridge,如果远端不添加0.0.0.0 就意味着只能bridge自己访问这个lab,如果添加了0.0.0.0就意味着可以其他机器可以通过bridge访问到)之后,你成功地将本地上的 22 端口反向转发到了 bridge 的 10022 端口。只要保持这个 ssh 不断,任何一台机器都可以首先连接到 bridge,然后通过 ssh user@localhost -p 10022 连回到 lab。

不过上面这么做并不稳健,如果因为网络波动导致 ssh -R 那个连接断了,那么从 bridge 就完全失去了到 lab 的控制。万幸的是,有一个叫做 autossh 的软件,可以自动的检测断线,并在断线的时候重连。在 Ubuntu 上你可以使用 sudo apt-get install autossh 来安装。

lab$ autossh -NfR 10022:localhost:22 bridge

上面这句话里面 -N 表示非不执行命令,只做端口转发;-f 表示在后台运行,-R 10022:localhost:22 就是把本地的22端口转发到远程的10022端口。我们也可以设置为开机时运行:在 /etc/rc.local 里面 exit 0 这句话之前加上

su - user -c autossh -NfR 10022:localhost:22 bridge

其中 user 是用户名。需要注意的是,开机时运行 autossh 需要配置公钥登入。

临时创建网站供查看

比如我在本地跑了一个网站,我想临时把我的网站发给朋友看看。在本地运行 python -m SimpleHTTPServer 即可在本地的8000端口启动一个网站,你可以在浏览器中通过 http://localhost:8000/ 看到。下面我们想让远方的朋友看到这个网站。

local$ ssh -NR 0.0.0.0:18000:localhost:8000 bridge

远方的朋友即可通过 http://bridge:18000/ 看到了。注意到这里和上面的命令有一个小小的不同,就是多了 0.0.0.0,这告诉 ssh,要把18000端口绑定在远端的所有IP上。如果像之前那样省略的话,缺省值是只绑定在 localhost,也就是只有在 jumpbox 本机才可以访问,而其他人都不能访问。

临时代理创建

比方说在本地的127.0.0.1:1080运行了HTTP代理服务,现在我想让另一台机子 remote 也能够使用这个HTTP代理。

local$ ssh -NR 11080:localhost:1080 remote
local$ ssh remote
remote$ export http_proxy=http://127.0.0.1:11080/
remote$ export https_proxy=http://127.0.0.1:11080/
remote$ curl http://ifconfig.co

用作 SOCKS5 代理

要是想要在家访问公司内网的一些网站,但是公司又没有提供进入内网的VPN,那怎么办呢?通过 ssh -D 可以在本地建立起一个 SOCKS5 代理:local$ ssh -ND 1080 workplace

 

【1】 ssh manual

试玩Chromebook Piexl

April 12th, 2016

Chromebook piexl是Google在2013年新出的一款超极本,他的外形十分小巧,内置Chorme OS系统,用户的操作处于云端,但是基于国内大家都懂得的网络环境,这款笔记本犹如鸡肋。配置清单如下:

Model Pixel (Wi-Fi)[19] Pixel (LTE) [19] Pixel (2015) [20] Pixel (LS) [20]
Release date February 2013 April 2013[21] March 2015 March 2015
Release price US$1299 US$1449 US$999 US$1299
Size 297.7 × 224.6 × 16.2 mm 297.7 × 224.55 × 15.3 mm
Weight 1.52 kilograms (3.4 lb) 1.5 kilograms (3.3 lb)
Processor Intel Core i5-3427U (dual-core 1.8 GHz) (CPU)
Intel HD Graphics 4000 (integrated) (GPU)
Intel Core i5-5200U (dual-core 2.2 GHz) (CPU)
Intel HD Graphics 5500 (integrated) (GPU)
Intel Core i7-5500U (dual-core 2.4 GHz) (CPU)
Intel HD Graphics 5500 (integrated) (GPU)
Memory 4 GB DDR3 RAM 8 GB DDR3 RAM 16 GB DDR3 RAM
Storage 32 GB Solid state 64 GB Solid state 32 GB Solid state 64 GB Solid state
Screen 12.85 in (326 mm)
2,560 × 1,700 (239 ppi)
3:2 (1.5:1) aspect ratio
400 nit brightness
178° viewing angle
Multi-touch
Gorilla Glass
Webcam 720p HD, integrated
Keyboard Backlit
Touchpad Clickable, etched-glass
Audio 3.5-mm combo headphone/microphone jack
3 built-in microphones
Integrated DSP (for noise cancellation)
Stereo speakers
Ports 2 × USB 2.0
Mini DisplayPort
SD/MMC card reader
2 × USB 3.1 Type-C (5Gbit/s data, power in, video out)
2 × USB 3.0 Type-A
SD/MMC card reader
WiFi 802.11a/b/g/n
Dual-band (2.4/5 GHz)
2×2 MIMO
802.11a/b/g/n/ac
Dual-band (2.4/5 GHz)
2×2 MIMO
Bluetooth Bluetooth 3.0 Bluetooth 4.0
Wireless LTE modem
Battery 59 Wh (5 hours active use) 72 Wh[22] (12 hours for average user behaviour)[22]
Included extras 1 TB Google Drive storage for 3 years
12 sessions GoGo Internet
100 MB/month free fromVerizon (US) (LTE only)
1 TB Google Drive storage for 3 years12 sessions GoGo Internet

我拿到手的是WiFi版本,可以看出CPU和Memeory在当时都算是主流本,唯一的问题就是内置的SSD太小了,只有32GB,装Windows肯定是不够的,所以我只好装了Ubuntu 14.04 LTS。默认情况Chromebook处于用户模式下,用户无权对系统进行修改,所以我们要先切换到开发者模式下:

  1. 同时按住Esc,F3和电源键,系统会自动重启,进入开发者模式
  2. 重启后,屏幕会显示系统处于开发者模式,不用管。直接Ctrl+D

18od6wnzwo855jpg

3. 这时已经进入系统(需要搭梯子)

Chromebook安装ubuntu有两种工具1.Crouton  2.硬盘安装。我个人喜欢硬盘安装,可以十分有效率的控制硬件。具体Crouton安装,可以参考:http://lifehacker.com/how-to-install-linux-on-a-chromebook-and-unlock-its-ful-509039343

硬盘安装的方式可能需要修改一些配置,因为Chromebook的SeaBios绑定了引导系统,所以我们必须关闭bios的安全引导功能,然后才可以安装启动ubuntu。首先我们打开终端Ctrl+Alt+T ,然后shell,然后运行sudo bash,这时我们就对系统有完整的控制权。这时我们

$crossystem
$crossystem dev_boot_usb=1
$crossystem dev_boot_legacy=1

然后重启,插入Ubuntu 安装盘,这时会进入Seabios,无视之,按下Ctrl+L && Esc即可。下面安装就大同小异了,推荐安装新版本内核,因为Chrombook拥有触摸屏和触摸板,在旧版本内核下可能无法使用,安装linux 4.0后的版本就很好的驾驭了,来张安装完成图:

IMG_1696

Chromebook只适合上网办公轻应用,遇到播放flash视频时,散热量非常大可能比较影响体验。

参考:

https://gbatemp.net/threads/how-to-install-windows-8-on-your-google-chromebook-pixel.350111/

使用kernel-package编译内核img包

October 11th, 2015

之前编译内核一般也使用make –> make modules_install –> make install –> update-grub 这一系列步骤,在Debian、Ubuntu机器上可以使用kernel-package来编译安装内核。kernel-package是Debian提供的一个编译Linux内核的一个工具集,安装kernel-package 会同时安装上build-essential、libncurses-dev、linux-source等一系列工具。

首先安装:# apt-get install kernel-package

安装完成后我们可以使用dpkg 查看一下:# dpkg -l

在打印出来的信息中我们可以看到,kernel-package 是 A utility for building Linux kernel related 也就是一个用来构建内核的工具。

$dpkg -l kernel-package
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                Version        Architecture   Description
+++-===================-==============-==============-===========================================
ii  kernel-package      12.036+nmu3    all            A utility for building Linux kernel related

我们会发现安装完kernel-package之后make等一系列工具也安装了,然后我们还是编译一个内核看一下kernel-package的作用:

# cd  linux-3.18.21    // 进入你想要编译的内核的解压文件夹
# make menuconfig   // 编译内核,自己选择
# sudo CONCURRENCY_LEVEL=4 make-kpkg --initrd kernel-image  // 这一句就是在使用kernel-package在编译。

CONCURRENCY_LEVEL=4 是设置多线程(类似于我们make -j4的多线程控制), make-kpkg就是kernel-package提供的编译工具,–initrd参数是说明在生成的image包里有initrd

……

dpkg --build      /home//kernel/linux-3.18.21/debian/linux-image-3.18.21
dpkg-deb: building package `linux-image-3.18.21' in `../linux-image-linux-3.18.21.Custom_i386.deb'.
make[2]: Leaving directory `/home/kernel/linux-3.18.21'
make[1]: Leaving directory `/home/kernel/linux-3.18.21'

从上面的信息可以看出,我们的make-kpkg生成了一个deb文件,其实这就是我们编译好的内核,放在当前内核文件夹的上层目录。到此我们的编译工作结束,我们可以使用dpkg 安装我们刚编译好的目录!如果你的编译的内核对其他机器也适用,你可以拷贝这个deb文件到其他机器上直接安装使用。这个工具使得我们编译内核工作变得更加简单快捷。使用kernel-package编译内核最大的好处是我们可以使用  dpkg -r 删除我们编译的内核。

 

安装我们使用的是 sudo dpkg -i linux-image-3.18.21.Custom_i386.deb
卸载时我们可以直接使用: sudo dpkg -r  linux-image-3.18.21