rust实战系列-base64编码
前言
某些只能使用ASCII字符的场景,往往需要传输非ASCII字符的数据,这时就需要一种编码可以将数据转换成ASCII字符,而base64编码就是其中一种。
编码原理很简单,将原始数据以3字节(24比特)为一组均分成4份,每部分6比特共64种组合,每种组合转换成对应字符,最后拼接起来即可。若最后一组不够3字节则后面用0补齐,转换后补齐多少字节就用几个“=”字符表示。
上面大致描述了base64编码的场景及原理,具体细节不做探讨,本文主要描述用rust实现时涉及的rust知识点。
标准输出读取
程序的数据是从标准输入(stdin)中读取的,使用std::io::stdin()返回实现Read特性(trait)的Stdin结构体,调用Read特性read函数即可从标准输出读取数据,例子如下。
let buf: [u8; 300] = [0; 300];
let size = stdin().read(&buf).unwrap();
read使用一个u8类型数组用作从标准输入接收数据的缓存,接收到的字节数以包裹在Result中的usize类型返回,这里简单地使用unwrap()解包获取字节数。
缓存的大小是固定的但是输入数据运行时确定的,因此使用循环不断从标准输入中读取数据,直到读取数据字节数为0。
let mut buf: [u8; 300] = [0; 300];
loop {
let size = stdin().read(&mut buf).unwrap();
if size == 0 {
break;
}
// Output the buffer, and assume that buffer is utf-8 string.
print!("{}", String::from_utf8(buf.to_vec()).unwrap());
}
IO抽象模型
与Java的InputStream和OutputStream一样,rust也有IO抽象模型,那就是Read和Write特性。
Read和Write特性将输入输出抽象为read、write等一系列函数,具体细节尤其实现决定。
使用时无需知道其实现是标准输入输出、文件还是网络,例如可以实现一个输入源自动匹配函数,当指定路径的文件不存在就读取标准输入,反之就从文件中读取内容。
fn main() {
let mut buf: [u8; 300] = [0; 300];
loop {
let size = input("./input").read(&mut buf).unwrap();
if size == 0 {
break;
}
print!("{}", String::from_utf8(buf.to_vec()).unwrap())
}
}
fn input(path: &'static str) -> Box<dyn Read> {
if !Path::new(path).exists() {
return Box::new(stdin());
}
Box::new(File::open(path).unwrap())
}
数组
rust数组是定长的,因此声明时必须明确长度及类型以便分配内存,长度和类型可以自动推断也可指定。
let arr: [i32; 4]; // 1. Specify type and length, the format is [Type; length].
let arr = [0, 4]; // 2. Infer type automatically.
let arr = [0, 0, 0, 0]; // 3. Infer type and length.
与其他多数语言一样也是使用下标访问元素,超出范围会直接panic。
let mut arr = [0, 0, 0, 0];
print!("{}", arr[0]); // Output is 0.
arr[0] = 1;
print!("{}", arr[0]); // Output is 1.
arr[4] = 4; // Panic here.
字符串
rust的字符串有str和String两种:
- str是原始类型,其实现是一种切片(Slice)类型且不可变,由于切片类型没有所有权,因此只能是以引用方式&str出现;
- String有所有权且可变,其使用的是堆内存,因此开销会比str大。
字符串相加是常见场景,一种方式是直接用+运算符,注意其左值必须是String类型,因为String实现了运算符重载的Add特性且由于其是可变的。
let a = String.from("a");
let b = "b";
let _ = a + b;
// Can not use variable "a" here, its ownership has been moved
注意这里作为左值的a变量在运算后不能在被使用,因为其所有权已经被移动。
另一种相加方式是String的push_str方法,其实+实现也是调用了此方法。
附录
base64编码实现完整代码如下:
use std::io::{stdin, Read};
fn main() {
let mut buf: [u8; 300] = [0; 300];
loop {
let size = stdin().read(&mut buf).unwrap();
if size == 0 {
break;
}
print!("{}", String::from_utf8(buf.to_vec()).unwrap());
print!("{}", encode(&buf, size));
}
}
fn encode(bytes: &[u8], size: usize) -> String {
let mut buf = String::new();
let i = 0;
for mut i in 0..(size / 3) {
i = i * 3;
let f = bytes[i];
let s = bytes[i + 1];
let t = bytes[i + 2];
buf.push_str(&cvt((f & 0xfc) >> 2));
buf.push_str(&cvt((f & 0x03) << 4 | ((s & 0xf0) >> 4)));
buf.push_str(&cvt((s & 0x0f) << 2 | ((t & 0xc0) >> 6)));
buf.push_str(&cvt(t & 0x3f));
}
let mut i = (i + 1) * 3;
i = if size < i { 0 } else { i };
let remain = size - i;
if remain == 1 {
let f = bytes[i];
buf.push_str(&cvt((f & 0xfc) >> 2));
buf.push_str(&cvt((f & 0x03) << 4 | 0));
buf.push_str("==");
} else if remain == 2 {
let f = bytes[i];
let s = bytes[i + 1];
buf.push_str(&cvt((f & 0xfc) >> 2));
buf.push_str(&cvt((f & 0x03) << 4 | ((s & 0xf0) >> 4)));
buf.push_str(&cvt((s & 0x0f) << 2 | 0));
buf.push_str("=");
}
buf
}
const BASE64_TABLE: [char; 64] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '+', '/',
];
fn cvt(i: u8) -> String {
BASE64_TABLE.get(i as usize).unwrap().to_string()
}
rust实战系列-base64编码的更多相关文章
- Rust实战系列-基本语法
本文是<Rust in action>学习总结系列的第二部分,更多内容请看已发布文章: 一.Rust实战系列-Rust介绍 " 主要介绍 Rust 的语法.基本类型和数据结构,通 ...
- rust实战系列 - 使用Iterator 迭代器实现斐波那契数列(Fibonacci )
为什么是斐波那契数列 斐波那契数列十分适合用来实战rust的迭代器,算法也很简单,一目了然.这个例子可以用来学习Iterator的使用,十分适合刚学习了rust的迭代器章节后用来练练手. 代码实战 d ...
- javacpp-FFmpeg系列之3: 像素图像数据转换(BGR与BufferdImage互转,RGB与BufferdImage互转,BufferdImage转Base64编码)
javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...
- SSE图像算法优化系列三十一:Base64编码和解码算法的指令集优化。
一.基础原理 Base64是一种用64个Ascii字符来表示任意二进制数据的方法.主要用于将不可打印的字符转换成可打印字符,或者简单的说是将二进制数据编码成Ascii字符.Base64也是网络 ...
- .NET Core加解密实战系列之——消息摘要与数字签名算法
目录 简介 功能依赖 消息摘要算法 MD算法 家族发展史 应用场景 代码实现 MD5 示例代码 SHA算法 应用场景 代码实现 SHA1 SHA256 示例代码 MAC算法 HMAC算法的典型应用 H ...
- .NET Core加解密实战系列之——对称加密算法
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- Spark入门实战系列--6.SparkSQL(上)--SparkSQL简介
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .SparkSQL的发展历程 1.1 Hive and Shark SparkSQL的前身是 ...
- Spark入门实战系列--9.Spark图计算GraphX介绍及实例
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理 ...
随机推荐
- DevOps、CI、CD都是什么鬼?
关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ DevOps DevOps是Development和Operations的组合,是一种方法论, ...
- Spring Ioc源码分析系列--前言
Spring Ioc源码分析系列--前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了.其次,作为一个Java开发者,Spring是绕不开的课题.在Spr ...
- 基于DEM的坡度坡向分析
坡度坡向分析方法 坡度(slope)是地面特定区域高度变化比率的量度.坡度的表示方法有百分比法.度数法.密位法和分数法四种,其中以百分比法和度数法较为常用.本文计算的为坡度百分比数据.如当角度为45度 ...
- python工具--获取盛科交换机端口模块类型,波长,传输距离等信息
交换机端口模块信息对应的OID节点为各厂商私有节点,获取其他厂商信息需要把OID进行替换 1 #! /usr/bin/env python 2 #-*-coding:utf-8-*- 3 import ...
- 使用 AgileConfig 动态配置 NLog
NLog 是我们在 .NET 领域使用非常广泛的日志组件.它默认使用 xml 来维护它的配置.最近有几个同学问我当使用 AgileConfig 的时候如何配置 NLog .因为 AgileConfig ...
- python闭包函数与装饰器
目录 闭包函数 闭包概念 实际应用 装饰器 简介 简单版本装饰器 进阶版本装饰器 完整版本装饰器 装饰器模板 装饰器语法糖 装饰器修复技术 问题 答案 闭包函数 闭包概念 闭:定义在函数内部的函数 包 ...
- java对象与Json字符串之间的转化
public class Test { public static void main(String[] args) { // 实现java对象与Json字符串之间的转化 // 1. Person对象 ...
- numpy中shape的部分解释
转载自:https://blog.csdn.net/qq_28618765/article/details/78081959和https://www.jianshu.com/p/e083512e4f4 ...
- tf.data(二) —— 并行化 tf.data.Dataset 生成器
在处理大规模数据时,数据无法全部载入内存,我们通常用两个选项 使用tfrecords 使用 tf.data.Dataset.from_generator() tfrecords的并行化使用前文已经有过 ...
- 为什么 C# 访问 null 字段会抛异常?
一:背景 1. 一个有趣的话题 最近在看 硬件异常 相关知识,发现一个有意思的空引用异常问题,拿出来和大家分享一下,为了方便讲述,先上一段有问题的代码. namespace ConsoleApp2 { ...