matlab循环遍历struct结构体

作者:matrix 发布时间:2019年6月27日星期四 分类:零零星星

图片4911-matlab循环遍历struct结构体
matlab网络请求操作完成之后需要对返回数据读取,操作时候出现的问题就是无法循环struct数据。
因为接口一般以json数据格式返回,matlab会把Object结构处理为struct数据而无法循环读取。很多字段的时候我可不希望看到手写矩阵声明。
取struct长度的sizelength都无法正常返回其元素个数,听说它不是向量。

办法

fieldnames函数获取struct的字段field数据(返回为cell元胞数组)
之后用structName.(expression)这种操作获取动态的struct数组数据。比如struct.a等同于动态操作的struct.('a')
现在才知道文档上有,我TM竟然随便点括号猜出来的~

%声明方式1
%s = struct('a',11,'b',222)

%声明方式2
s=struct
s.a = 11
s.b = 222

% https://ww2.mathworks.cn/help/matlab/ref/struct.html

fileds = fieldnames(s);

for i=1:length(fileds)
    k = fileds(i);
    key = k{1};
    value = s.(key);

    disp(key)
    disp(value)
    disp('---')
end

% s.('a') %动态结构体引用

升级操作:自动生成对应变量

按照Struct字段自动生成对应的变量名和值

s = struct('a',11,'b',222)

fileds = fieldnames(s);

for i=1:length(fileds)
    k = fileds(i);
    key = k{1};
    value = s.(key);
    eval(['variable_',num2str(key) ' = ' num2str(value)])
end

说明:
使用eval函数动态创建variable_%s的变量。
本来在mathworks上看到有人发布过Structure fields to variables方法来自动处理struct,但是没有正版授权码 所以无法下载使用。

参考:
https://ww2.mathworks.cn/help/matlab/learn_matlab/types-of-arrays.html
https://ww2.mathworks.cn/help/matlab/ref/struct.html
https://www.ilovematlab.cn/thread-317868-1-1.html

https://ww2.mathworks.cn/matlabcentral/fileexchange/26216-structure-fields-to-variables

PEACE~

Matlab网络请求操作

作者:matrix 发布时间:2019年6月22日星期六 分类:零零星星

matlab这个学术界的编程语言我是几个月前听说,有些基本操作是真的麻烦,当然在其他方面matlab是相当🐂🍺

使用Matlab进行GET/POST请求调用接口,提交参数。

之前苦于没有将get请求的query参数进行变量提取,好在www.mathworks.com找到有文档说明

GET请求操作函数封装

get网络请求封装成函数get_request

%GET网络请求
function [result]=get_request(uri,query)

    import matlab.net.* %导入Matlab网络请求库
    import matlab.net.http.*

    uri = URI(uri);%请求地址;
    uri.Query = matlab.net.QueryParameter(query);%get  附加请求参数 

    r = RequestMessage;
    r.Method = 'GET';%使用GET请求类型

    response = send(r,uri);%发送请求
    status = response.StatusCode%获取服务器响应的状态码
    if (status==200) %一般成功响应的状态码为200 表示ok 即成功
        content = response.Body.Data; %获取服务器响应的内容
        result = content;
    else
        disp('请求失败')
    end

说明:

参数1 uri: 请求url, 如:http://hhtjim.com
参数2 query: get请求参数 传入struct类型数据
还有就是接口返回json数据(response响应头有指定type json),matlab会自动解析json数据,最后用response.Body.Data调用就好,不方便查看的那就debug。

如果要手动处理json字符串的话使用jsonencode, jsondecode进行编解操作。

调用举例:
请求:http://hhtjim.com?arg=123

get_request('http://hhtjim.com',struct('arg', '123'))

或者

query.arg = '123'
get_request('http://hhtjim.com',query)

上面两种方式的调用都可以完成操作。matlab内部会自己封装拼接为http://hhtjim.com?arg=123进行请求。

如果想要更简单的ge请求可以使用webread进行操作。

POST请求

这里测试的POST的请求更为复杂,会添加自定义请求头,post body表单内容,cookie的涉及。所以没有封装成统一调用的函数,需要的自行修改咯~

import matlab.net.* %倒入Matlab网络请求库
import matlab.net.http.*

uri = URI('http://localhost:8080/testOrder/double_param/');%请求地址

%添加请求参数
qStruct.K=1;
qStruct.d2=2;
qStruct.d1=3;
qStruct.L=4;
qStruct.order_switch=1;%1开 0关
qStruct.order_size=2;
qStruct.diff_earnings=1.00009;



r = RequestMessage;
r.Method = 'POST'%使用POST请求类型
csrf = 'YYC0e8GICcEroZGDuL8THJ4ZQdwQpDqNtkBsfnaBP0XpH3rqYVNXADJGpWdo53o0'%Djabgo框架表单中防止跨站请求的参数,其实这是服务器生成的伪随机字符
qStruct.csrfmiddlewaretoken=csrf;
r.Body = matlab.net.QueryParameter(qStruct)%放入请求参数

%添加请求头
r = addFields(r,'Content-Type','application/x-www-form-urlencoded');
r = addFields(r,'Cookie',sprintf('csrftoken=%s',csrf));


response = send(r,uri);%发送请求
status = response.StatusCode%获取服务器响应的状态码
if (status==200) %一般成功响应的状态码为200 表示ok 即成功
    content = response.Body.Data %获取服务器响应的内容
    disp('请求成功:')
    disp(content)
else
    disp('请求失败')
end

%参考:
%https://ww2.mathworks.cn/help/matlab/http-interface.html
%https://ww2.mathworks.cn/help/matlab/ref/matlab.net.http.requestmessage.send.html#bu199af-6
%https://www.mathworks.com/help/matlab/ref/matlab.net.http.message.addfields.html
%https://ww2.mathworks.cn/help/matlab/ref/matlab.net.queryparameter-class.html

最后我真想说术业有专攻,用matlab进行网络请求是的自讨苦吃。

参考:

https://ww2.mathworks.cn/help/matlab/ref/matlab.net.uri-class.html#bvflp65-2

https://ww2.mathworks.cn/help/matlab/http-interface.html
https://ww2.mathworks.cn/help/matlab/ref/matlab.net.http.requestmessage.send.html#bu199af-6
https://www.mathworks.com/help/matlab/ref/matlab.net.http.message.addfields.html
https://ww2.mathworks.cn/help/matlab/ref/matlab.net.queryparameter-class.html

ISO8601时间字符串到时间戳处理

作者:matrix 发布时间:2019年6月21日星期五 分类:Python 兼容并蓄

之前不太理解ISO8601时间格式,后来看了下网上文章,其实是没有固定的单一格式。
按照下面这些其实都属于ISO8601时间格式:

2019-03-25T16:00:00.000111Z
2019-03-25T16:00:00.111Z
2019-03-25T16:00:00Z
2019-03-25T16:00:00
...

Z表示祖鲁时间Zulu time+0时区,若去掉不写Z则采用系统本地时区。
ISO8601时间还有很多其他扩展格式。

下面代码处理的也就是普通格式

python

import datetime,pytz
def iso2timestamp(datestring, format='%Y-%m-%dT%H:%M:%S.%fZ',timespec='seconds'):
    """
    ISO8601时间转换为时间戳

    :param datestring:iso时间字符串 2019-03-25T16:00:00.000Z,2019-03-25T16:00:00.000111Z
    :param format:%Y-%m-%dT%H:%M:%S.%fZ;其中%f 表示毫秒或者微秒
    :param timespec:返回时间戳最小单位 seconds 秒,milliseconds 毫秒,microseconds 微秒
    :return:时间戳 默认单位秒
    """
    tz = pytz.timezone('Asia/Shanghai')
    utc_time = datetime.datetime.strptime(datestring, format)  # 将字符串读取为 时间 class datetime.datetime

    time = utc_time.replace(tzinfo=pytz.utc).astimezone(tz)

    times = {
        'seconds': int(time.timestamp()),
        'milliseconds': round(time.timestamp() * 1000),
        'microseconds': round(time.timestamp() * 1000 * 1000),
    }
    return times[timespec]


def timestamp2iso(timestamp, format='%Y-%m-%dT%H:%M:%S.%fZ'):
    """
    时间戳转换到ISO8601标准时间(支持微秒级输出 YYYY-MM-DD HH:MM:SS.mmmmmm)
    :param timestamp:时间戳,支持 秒,毫秒,微秒级别
    :param format:输出的时间格式  默认 iso=%Y-%m-%dT%H:%M:%S.%fZ;其中%f表示微秒6位长度

    此函数特殊处理,毫秒/微秒部分 让其支持该部分的字符格式输出
    :return:
    """
    format = format.replace('%f','{-FF-}')#订单处理微秒数据 %f
    length = min(16, len(str(timestamp)))#最多去到微秒级

    #获取毫秒/微秒 数据
    sec = '0'
    if length != 10:#非秒级
        sec = str(timestamp)[:16][-(length - 10):]#最长截取16位长度 再取最后毫秒/微秒数据
    sec = '{:0<6}'.format(sec)#长度位6,靠左剩下的用0补齐
    timestamp = float(str(timestamp)[:10])#转换为秒级时间戳
    return datetime.datetime.utcfromtimestamp(timestamp).strftime(format).replace('{-FF-}',sec)

说明:
之前别个写的iso到时间戳的转换方法简直蛋疼,无参数说明和无法精确到秒级别。
两个函数都可以相互转换和处理。

参考:

https://en.wikipedia.org/wiki/ISO_8601
https://docs.Python.org/zh-cn/3.7/library/datetime.html?highlight=isoformat#strftime-strptime-behavior
https://www.w3.org/TR/NOTE-datetime
https://www.cryptosys.net/pki/manpki/pki_iso8601datetime.html

ssh config多账户/多域名配置

作者:matrix 发布时间:2019年6月18日星期二 分类:Linux

测试环境:ubuntu

客户端连接远程ssh/git服务的时候可以在本地配置SSH config,用于简化多参数使用操作或者修改默认的ssh命令使用的配置。

我这里需要使用gitee的ssh密钥来管理远程仓库代码,方便以后可以免密码提交/更新代码。然而本地已经存在一个~/.ssh/id_rsa私钥,且还设置了用来保护私钥的passphrase密码。如果用之前的私钥连接gitee会造成每次都要求输入passphrase密码,亦或不能单独区分使用。
这个问题可以使用配置文件~/.ssh/config来解决

新建新的密钥对

$ ssh-keygen -t rsa -C "user"

新建的时候设置新密钥的保存路径,避免把之前的覆盖掉

配置

config文件默认是不存在的,直接新建即可

$ vi ~/.ssh/config

使用下面配置:

# gitee账户
Host gitee.com
HostName gitee.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rrsa_gitee

说明:
Host类型昵称,可以简化登录的输入地址,比如Host ccl,则可以用ssh ccl直接连接
HostName表示连接的远程主机地址
IdentityFile表示指定私钥文件路径
还有其他参数
Port指定端口
User指定用户名

这种配置可以让ssh来根据远程host地址来使用不同的私钥,设置了User还可以让ssh工具不同用户名来读取配置,也可以使用相同host地址哟~

比如都是github的不同账户,类似配置:

Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/user1_rsa
User user1


Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/user2_rsa
User user2

参考:
https://gitee.com/help/articles/4229
http://vra.github.io/2017/07/09/ssh-config/
https://gitee.com/help/articles/4181
https://daemon369.github.io/ssh/2015/03/21/using-ssh-config-file

PEACE~

Python递归中使用协程yield

作者:matrix 发布时间:2019年6月11日星期二 分类:Python

修改递归函数用于遍历目录中文件。

普通操作

def recursive_open_file(path):
    rel = []
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                rel.append(new_dir)
        else:
            rel = rel + recursive_open_file(new_dir)
    return rel

# print(type(recursive_open_file(dir))) # <class 'list'>

调试yield

想用协程目的是为了想让程序找到相关文件之后中断挂起然后返回数据,避免一次性加载全部资源在内存中。

之前是想的太简单,没用过果真是不知道。

错误版本

def recursive_open_file(path):
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                yield new_dir
        else:
            yield recursive_open_file(new_dir)

#执行
for i in recursive_open_file(dir):
    print(i) #无法获取文件路径

说明:
yield recursive_open_file(new_dir)返回给外部调用层的数据为<generator object recursive_open_file at 0x10f7765e8> ,不是想要的String!!

正常版本

内部再迭代下就好了 🙈

def recursive_open_file(path):
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                yield new_dir
        else:
            for i in recursive_open_file(new_dir):
                yield i

PHP协程递归同理


function openDirectory($path) { $dir = dir($path); while (false != ($entry = $dir->read())) { if ($entry != "." && $entry != "..") { $n_path = $path . DIRECTORY_SEPARATOR . $entry; if (is_dir($n_path)) { foreach (openDirectory($n_path) as $i){ yield $i; } } else { yield $n_path; } } } } //调用执行 $dir = '/Users/panc/Desktop/Python/testfile'; foreach (openDirectory($dir) as $item){ print_r($item); print_r("\n"); }

协程send操作

按照廖雪峰的协程教程的生产者-消费者模式:

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)#启动
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()#关闭

c = consumer()
produce(c)

笔记:
c = consumer()不会执行consumer函数,因为内部有yield关键字,consumer函数是生成器generator对象。
通过多个断点调适可以看到yield处的代码会中断执行,然后切换到起调函数的位置继续执行
yield r相似于return返回数据,返回给send()方法返回值。
send(n)操作是把n发送给yield r的返回值
c.send(None)用于启动consumer函数,程序会进入while True循环,在yield处中断

这里yield操作相比较递归遍历那头来说更加麻烦些,因为执行的时候会在两个函数之间相互切换,互相发送数据,需要send方法来启动生成器generator对象。consumer内部因为是while true,所以记得要关闭c.close()

参考:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824
https://github.com/Earthson/RecGen
https://blog.csdn.net/mieleizhi0522/article/details/82142856

https://blog.51cto.com/xtceetg/1874982

PEACE~