解决Excel文件密码格式化CSV问题的技巧

<!– wp:heading –> <h2 class=“wp-block-heading”>===INTRO:===</h2> <!– /wp:heading –><!– wp:paragraph –> CSV(Comma-Separated Values)文件是常用的数据存储格式之一。在使用Excel工具进行数据操作时,密码格式化CSV文件可能会导致无法打开或修改文件。本文提供了一些技巧,解决Excel文件密码格式化CSV问题。 <!– /wp:paragraph –><!– wp:heading –> <h2 class=“wp-block-heading”>===解决密码格式化CSV问题的方法===</h2> <!– /wp:heading –><!– wp:list {“ordered”:true} –> <!– wp:list-item –>1. 密码去除:在使用VBA代码去除密码时需要注意,密码不能为空。可以使用以下代码去除密码: <!– /wp:list-item –> <!– /wp:list –><!– wp:code –> <pre class=“wp-block-code”><code>Sub RemovePassword() Dim objFile As FileObject Dim strPath As String strPath = “C:UsersusernameDocumentsfile.csv” Set objFile = ThisWorkbook.FileObjects.Open(strPath) objFile.LockExcel = False objFile.CloseEnd Sub</code></pre> <!– /wp:code –><!– wp:list {“ordered”:true,“start”:2} –> <ol start=“2”><!– wp:list-item –><li>密码重置:如果您需要重置密码,可以使用以下代码:</li><!– /wp:list-item –></ol> ...

April 14, 2024 · 2 min · jiezi

关于csv:正确处理-CSV-文件的引号和逗号

CSV(Comma-Separated Values,逗号宰割值),就是用纯文本的模式存储表格数据,最大的特点就是不便。 作为开发,咱们常常面临导数据的问题,特地是后盾零碎,产品或者经营的共事经常会提需要。 Emmm,瞎话说,间接用 PHPExcel 也是 OK 的,不论是 WPS Office 或者微软 Office,都能完满反对。 但我还是比拟喜爱 CSV,起因是容易实现。有时候跑脚本、写爬虫抓数据,纯文本拼接后输入真的十分难受。 当我遇到了几个问题: 发现如果原来的文本带有回车或者换行,拼接后整行就断开了;加引号能够解决,然而引号两头有引号怎么办?用 \ 本义也不行;逗号怎么办?于是翻了谷歌,看到维基百科有 逗号分隔值 的标准化定义: 以(CR/LF)字符完结的DOS格调的行(最初一行可选)。一条可选的表头记录(没有牢靠的形式来检测它是否存在,所以导入时必须审慎)。每条记录“该当”蕴含同样数量的逗号分隔字段。任何字段都能够被包裹(用双引号)。蕴含换行符、双引号和/或逗号的字段该当被包裹。(否则,文件很可能不能被正确处理)。字段中的一个(双)引号字符必须被示意为两个(双)引号字符。如同也不难 :) 要害就是最初那两点——不多说,间接看代码: function csv_string($s){ return '"'. str_replace('"', '""', trim($s)) .'"';}$value1 = csv_string($value1);$value2 = csv_string($value2);$value3 = csv_string($value3);$line = "{$value1},{$value2},{$value3}\r\n";简略粗犷。 文章来源于自己博客,公布于 2018-06-02,原文链接:https://imlht.com/archives/145/

June 18, 2023 · 1 min · jiezi

关于csv:团队效率提升PythonPyWebIO介绍

作者:京东批发 要害Q&A疾速理解PyWebIO Q:首先,什么是PyWebIO? A:PyWebIO提供了一系列命令式的交互函数,可能让咱们用只用Python就能够编写 Web 利用, 不须要编写前端页面和后端接口, 让繁难的UI开发效率大大提高(自己非研发,用词可能不妥,大家轻点喷) Q:其次,咱们能用来干嘛?? 这对一个团队的效率晋升有什么作用?? A:Pywebio的作用在于让咱们能够疾速的开发一个带有UI界面的,反对用户输出的,以既定的逻辑输入后果的利用。 那么,咱们是不是能够将团队内一些机械性的数据处理,数据异动剖析等的工作以既定逻辑的形式通过Pywebio输入一个可复用的利用给大家应用呢? 当然,日常的数据经营过程中,咱们必定不是面对着变化无穷的case。 那么,咱们是不是能够用不同参数输出的形式来达到肯定的泛用性拓展呢? 只有,case和case之间的底层逻辑是统一的,咱们就能够用同一套逻辑,不同的入参来达到不同后果输入的获取。 Exampl 假使,咱们每天都有一项工作,每天对着一份又一份业务反馈的订单,而后部门须要对着这些订单自身进行一个初步的危险分层,咱们是不是能够把危险分层的底层规定写在后端,而后通过PywebIO来反对不同状况下的不同规定阈值输出, 疾速获取咱们所须要的危险分层后果。 (当然,如果数据容许,间接写SQL也能够,可是,SQL须要肯定的门槛,而PywebIO则能够通过UI的形式分享给那些没有技术背景的经营人员进行0代码应用。) 以下正式开始用一个例子来逐渐介绍PywebIO拓展包 简介虚构背景: 每天须要一份又一份地对业务反馈的样本来进行危险分层,为了进步解决效率。 打算计划: 通过现有危险标签的波尔标签,非波尔标签体系来搭建一个反对 灵便配置阈值来疾速获取分层后果的UI利用。 计划简介:根本逻辑如下,(以下均为举例所示,并不代表该计划就能够进行危险分层哈,大家请留神) 开始实现:这里的例子采取的是非数据库模式,反对的是上传本地csv,而后进行阈值配置。 Step one:本地文件上传首先,必定是得先文件上传的代码。 ##例子如下:import pandas as pdfrom pywebio.input import *from pywebio import start_serverfrom pywebio.output import *import nest_asyncioimport numpy as npimport osimport timenest_asyncio.apply()import pandas as pdfrom pywebio.input import *from pywebio import start_serverfrom pywebio.output import *import nest_asyncioimport numpy as npimport osimport timenest_asyncio.apply()def read_csv(): put_markdown('# 只反对pin') put_markdown('性能如下:') put_markdown("""- 抉择与程序再**同一文件夹**的文件- 输出你心愿卡的危险值阈值 **不输出则默认-10**- 主动加载解析输入极黑标签占比以及明细数据- 请勾选你所须要的标签**(不勾选=全选)**,而后点击提交即可 """) file = file_upload('只反对上传该程序所在文件夹的csv文件哦', '.csv') ## 本地文件 raw_data = pd.read_csv(os.getcwd() + "\" + file['filename'], encoding='gbk') put_html(raw_data.to_html()) if __name__ == '__main__': start_server(read_csv, port=8081, debug=True, cdn=False, auto_open_webbrowser=True)容许代码后,因为” auto_open_webbrowser=True“,所以自动弹出一个WebUI,如下左图,抉择上传的文件,即可看到下右图的文件数据 ...

January 6, 2023 · 2 min · jiezi

关于csv:使用-csv-导入的方式在-SAP-S4HANA-里创建-employee-数据

SAP S/4HANA 有个 Fiori 利用叫做 Import Employees: 抉择 Employee and Employment Import, 点击 Download Templates: 本地保护好 EmployeeData_Template.csv 和 EmploymentData_Template.csv 两个文件后,点击 Import 按钮即可。 留神,举荐在 Visual Studio Code 里装置和 csv 文件相干的扩大 Rainbow CSV 之后再进行编辑,这样 csv 具备高亮成果。 常见的导入谬误:Email is mandatory for Employment 因为 csv 文件内没有保护邮件地址。 最初导入胜利: 可能在 Maintain Business users 利用里,搜寻到刚刚创立胜利的 Business User: 更多Jerry的原创文章,尽在:"汪子熙":

July 22, 2021 · 1 min · jiezi

spring-retry-guava-retrying-的整合sisyphus-java-重试框架

Sisyphus支持过程式编程和注解编程的 java 重试框架。 特性支持 fluent 过程式编程基于字节码的代理重试基于注解的重试,允许自定义注解无缝接入 spring接口与注解的统一解决 spring-retry 与 guava-retrying 中的不足之处设计目的综合了 spring-retry 和 gauva-retrying 的优势。 调整一些特性,使其更利于实际使用。 采用 Netty 类似的接口思想,保证接口的一致性,和替换的灵活性。 借鉴 Hibernate-Validator 的设计,允许用户自定义注解。 spring-retry 与 guava-retrying 中的不足之处更新记录更新记录开源地址sisyphus快速开始引入<plugin> <groupId>com.github.houbb</groupId> <artifactId>sisyphus-core</artifactId> <version>0.0.6</version></plugin>入门代码详情参见 [RetryerTest]() public void helloTest() { Retryer.<String>newInstance() .retry(new Callable<String>() { @Override public String call() throws Exception { System.out.println("called..."); throw new RuntimeException(); } });}代码分析retry指定一个 callable 的实现。 我们打印一条日志,并且模拟一个程序异常。 日志信息日志信息 called...called...called...和一些其他异常信息。 重试触发的条件,默认是程序发生了异常 这里的重试间隔默认为没有时间间隔,一共尝试3次。(包括第一次程序本身执行) 为什么选择 sisyphus为什么选择 sisyphus作为开发者,我们一般都会选择比较著名的框架。 比如 guava-retrying spring-retry。 或者干脆自己写一个。 为什么不是 guava-retrying/spring-retryjava retry 这篇文章中我列举了常见的实现方式以及上述的两种框架,也讲述了其中的不足。 ...

June 21, 2019 · 3 min · jiezi

基于-java-注解的-csv-读写框架更加简单灵活

CSV基于 java 注解的 csv 读写框架。 相关框架Apache commons-csv super-csv 简单看了下,这两个框架提供的特性都非常的基础。 创作原由以前觉得 csv 文件的读写非常简单,就懒得封装。 最近一个月写了两次 csv 文件相关的东西,发现要处理的细节还是有的,还浪费比较多的时间。 比如: UTF-8 中文编码使用 excel 打开乱码,因为缺少 BOM 头。不同类型字段转化为字符串,顺序的指定,head 头的指定,如果手写都会很繁琐。读取的时候最后 , 后无元素,split 会缺失等。为了解决上述问题,此框架应运而生。 特性Fluent 流式写法基于 java 注解,支持自定义的转换和灵活配置内置 8 大基本类型以及 String 类型转换解决 Excel 直接打开,utf-8 乱码问题支持集合、数组、Map 的存取支持对象中内嵌其他对象支持特殊字符转义变更日志CHANGE_LOG.md 开源地址csv 快速开始环境jdk7+ maven 3.x maven 引入<dependency> <groupId>com.github.houbb</groupId> <artifactId>csv</artifactId> <version>0.0.6</version></dependency>示例代码User.java演示基本类型的转换 public class User { private String name; private int age; private float score; private double money; private boolean sex; private short level; private long id; private char status; private byte coin; //Getter & Setter & toString()}对象列表构建 /** * 构建通用测试列表 * @return 列表 */ private List<User> buildCommonList() { User user = new User(); short s = 4; byte b = 1; user.age(10) .name("你好") .id(1L) .score(60) .coin(b) .level(s) .money(200) .sex(true) .status('Y'); return Arrays.asList(user); }写入测试代码public void commonTest() { final String path = "src\\test\\resources\\common.csv"; CsvWriteBs.newInstance(path) .write(buildCommonList());}文件生成name,age,score,money,sex,level,id,status,coin你好,10,60.0,200.0,true,4,1,Y,1读取public void commonTest() { final String path = "src\\test\\resources\\common.csv"; List<User> userList = CsvReadBs.newInstance(path) .read(User.class); System.out.println(userList);}日志信息[User{name='你好', age=10, score=60.0, money=200.0, sex=true, level=4, id=1, status=Y, coin=1}]引导类为什么需要引导类为了灵活的配置和默认配置并存,使用工具类会大大降低灵活性。 ...

June 20, 2019 · 4 min · jiezi

CSV03-csv-读写框架支持数组MapCollection-等常见集合

集合类有时候对象中会包含数组、Map、Collection 等常见集合。 为了存储的便利性,默认提供集合的相关支持。 特性和普通字段保持一致,如果指定注解转换,则以注解为准。 使用示例UserCollection.java用于演示集合的对象 public class UserCollection { private String[] arrays; private LinkedList<String> lists; private Map<String, String> maps; private Set<String> sets; //Getter/Setter/toString()}存储待存储对象的构建/** * 构建基于集合的测试列表 * @return 列表 * @since 0.0.3 */private List<UserCollection> buildCollectionList() { UserCollection user = new UserCollection(); String[] arrays = new String[]{"a", "b", "c"}; LinkedList<String> lists = new LinkedList<>(Arrays.asList(arrays)); Map<String, String> maps = new HashMap<>(); maps.put("key", "value"); maps.put("key2", "value2"); Set<String> sets = new HashSet<>(); sets.add("set1"); sets.add("set2"); user.setLists(lists); user.setArrays(arrays); user.setMaps(maps); user.setSets(sets); return Arrays.asList(user);}执行存储public void collectionTest() { final String path = "src\\test\\resources\\collection.csv"; CsvWriteBs.newInstance(path) .write(buildCollectionList());}存储效果arrays,lists,maps,setsa|b,a|b|c,key2=value2|key=value,set1|set2读取测试类public void collectionTest() { final String path = "src\\test\\resources\\collection.csv"; List<UserCollection> userList = CsvReadBs.newInstance(path) .read(UserCollection.class); System.out.println(userList);}测试日志[UserCollection{arrays=[a, b], lists=[a, b, c], maps={key=value, key2=value2}, sets=[set2, set1]}]注意为了保证 csv 以 , 分隔的统一性。 ...

June 17, 2019 · 1 min · jiezi

关于写作那些事之终于还是无法忍受纯人工统计数据

背景作为正在探索如何写作并发表到各大博客平台的新人,目前虽然已基本弄清写作和发表的基本流程,但是离打造个人知名度还差很大很大一段距离.尤其处于新手阶段,需要的更是自信与外界的积极反馈,看着各平台日益增长的阅读量和粉丝量,心中自然不甚欣喜.但是,持续的技术输出能否坚持下去很大程度上靠的是外界的积极反馈,如果写的文章基本没人看,或者反映并不理想,估计也很难再坚持创作了.所以笔者每天晚上都会统计一下各个平台的数据,看一下有哪些收获,只有看得见的数据才能给我安全感和自信心.下面简单展示一下每日数据统计效果:每日数据统计这里列出的平台默认是没有提供数据分析功能,而有些平台已经提供数据分析,说不定还要丰富图表分析功能,自然不用像下面这般复杂操作.所以,针对没有提供数据分析的平台,只好采用人工方式进行每日数据统计,一开始文章比较少,用肉眼加计算器就能很轻松得到阅读量和粉丝数等数据.但是,随着文章的每日更新,不断累加的文章越来越多,人工方式简直让我崩溃,比如昨晚在统计慕课网手记相关数据时就意外被一旁的小侄子打断三次!简直不可忍受,穷则思变,懒则想法偷懒,所以是时候探索新的方式解决纯手动的弊端了!全网汇总数据慕课手记简书博客园腾讯云社区图表渲染效果来自 gitbook 的 chart 插件,详情请参考 官网文档懒则想法偷懒回顾操作流程要想解放重复劳动量,必须先知道问题瓶颈,现在先回顾一下手动操作流程.登录各大博客平台后台,找到文章列表.打开计算器按照阅读量等指标累加每篇文章的相关数据.更新统计页面数据,利用 chart 插件渲染图标.修改 chart 渲染数据语法,截图渲染效果.确认渲染效果并推送到 github 网站本来不必利用截图表示图表的,只是无奈 github 不支持 chart 插件语法,只好用截图代替了.思考问题瓶颈分析上述流程后不难发现,最复杂也是最费时费力的便是第二步的数据统计,由于要肉眼统计文章并用计算器累加,简直是手脑并用,只有高度专注才能保证统计数据的准确性和可靠性.这也就解释了被打断三次后的崩溃了,找到问题的根源了,想办法如何解决吧!最容易想到的解决办法是手动复制文章列表数据,然后程序分析提取关键数据,最后再统计数据.又是三步操作,再分解一下步骤,看看目前能够解决哪部分.手动复制文章列表数据程序分析提取关键数据统计指标数据在这三步中,只有第二步最为关键,也是目前我能做到的事情,因为第一步可能需要爬虫技术或模拟接口调用,总体来说,总体来说还是比较麻烦的,以后再继续优化吧.梳理操作流程因此,现在先着手如何将复制后的文章列表转化成程序能够处理的文件格式,进而调用程序统计.下面以慕课网手记文章为例,简单介绍下处理流程.手动复制文章现在文章已复制到文件,应该保存成什么格式呢?这又是一个思考点.由于文件内容最终需要被程序处理,而程序处理要求数据需要具备一定的格式,因此自然不能是 txt 或 word 这类文档,平常接触比较多的文档数据处理一般就是 excel 或者 json 类型的文档.这里需要 excel 这种格式文档,但是 excel 比较笨重,还需要相关软件才能打开 excel 文件,好像并不是很适合,怎么办呢?但是我真的需要这种一行一行的数据格式啊,有没有折中的处理方案?当然有!轻量级的 csv 格式不是巧合适合简单文档处理吗?csv 和 excel 具有类似的特征,大体上都是一行一行一列一列地存储数据,最适合统计数据了.看着乱七八糟的文章列表,csv 也无法处理这种复杂数据啊,接下来还是要手动格式化数据,整理一下数据.程序分析提取至此,我们已经完成数据分析的第一步了,接下来是如何读取 csv 文件,由于本人是 java 程序员,所以我要看一下 java 如何处理 csv 文件.需求很简单,编写一个 csv 工具类并实现基本的写入和读取操作即可.说到工具类当然首选现成的开源工具了,毕竟小小的需求不值得造轮子.寻求解决方案说到开源工具,脑海中第一个闪现的是 Apache Commons 工具类,所以先去 maven 上搜一下有没有 csv 相关的工具类.在线搜索 commons-csv天不负我!果然有 csv 相关工具类,下面就开始研究如何调用吧!集成 commons-csv 工具类<!– https://mvnrepository.com/artifact/org.apache.commons/commons-csv –><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.6</version></dependency>编写工具类/** * 写入csv文件 * * @param data 数据内容 * @param filePath 文件路径 * @throws IOException /public static void writeCsv(List<String[]> data, String filePath) throws IOException { FileWriter fw = new FileWriter(new File(filePath)); final CSVPrinter printer = CSVFormat.EXCEL.print(fw); printer.printRecords(data); printer.flush(); printer.close();}/ * 读取csv文件 * * @param filePath 文件路径 * @return CSVRecord 迭代对象 * @throws IOException /public static Iterable<CSVRecord> readCSV(String filePath) throws IOException { InputStream inputStream = new FileInputStream(filePath); InputStreamReader isr = new InputStreamReader(inputStream); Iterable<CSVRecord> records = CSVFormat.EXCEL.parse(isr); return records;}/ * 测试写入并读取csv 文件 /private static void testWriteAndRead() throws IOException { //写入数据 List<String[]> data = new ArrayList<String[]>(); data.add(new String[]{“张三”, “18”, “3000”}); data.add(new String[]{“李四”, “20”, “4000”}); data.add(new String[]{“王二”, “25”, “5000”}); //写入文件路径 String path = “/Users/sunpo/Downloads/testWriteAndRead.csv”; //写入 csv 文件 writeCsv(data, path); //读取文件 Iterable<CSVRecord> records = readCSV(path); for (CSVRecord record : records) { for (String string : record) { System.out.print(string); System.out.print(" “); } System.out.println(); }}测试写入并读取功能测试结果真实可用,工具类基本功能编写完成.制定解决方案已经有了 csv 工具类,那么现在就要想办法解决实际问题,再看一下当前慕课网手记的内容格式吧!148浏览 2推荐 0评论204浏览 2推荐 0评论181浏览 2推荐 0评论分析上述内容格式有以下特点:内容数据一行一条数据,可能需要换行符问题每一行数据以空格分割,可分割成数组或列表再处理已分割后的列表项包括了有效数据和文字说明,可能需要过滤出有效数据按照上述分析结果,开始 coding 逐个解决,下面展示下关键代码.按照空格将每一行数据分割成列表List<String> row = StringTools.splitToListString(string, " “);StringTools.splitToListString 方式是笔者封装的分割字符串方法,目的将字符串按照指定分隔符分割成字符串列表处理分割后字符串列表并过来出有效数据String readCountWithDescString = row.get(0);String readCountString = StringUtils.substringBefore(readCountWithDescString, “浏览”);String recommendCountWithDescString = row.get(1);String recommendCountString = StringUtils.substringBefore(recommendCountWithDescString, “推荐”);String commentCountWithDescString = row.get(2);String commentCountString = StringUtils.substringBefore(commentCountWithDescString, “评论”);StringUtils.substringBefore 方法也是Apache Commons 工具类,具体来源于 org.apache.commons.lang3 ,下述涉及到的 StringUtils 静态方法 也是,不再单独说明.最后一步即统计分析//浏览数int readCount = 0;//推荐数int recommendCount = 0;//评论数int commentCount = 0;readCount += Integer.parseInt(readCountString);recommendCount += Integer.parseInt(recommendCountString);commentCount += Integer.parseInt(commentCountString);实施解决方案如此一来,三步均已解决,现在运行以下统计方法,看一下真实效果如何./* * 统计慕课手记 * * @throws IOException */private static void countImooc() throws IOException { //昨日统计数据 String yesterday = DateFormatUtils.format(DateUtils.addDays(new Date(), -1), “yyyyMMdd”); String path = String.format("/Users/sunpo/Documents/workspace/count/imooc-%s.csv”, yesterday); //总行数 int allRows = 0; //有效行数 int allValidRows = 0; //当前行是否有效 boolean isValidRow = true; //浏览数 int readCount = 0; //推荐数 int recommendCount = 0; //评论数 int commentCount = 0; Iterable<CSVRecord> records = readCSV(path); for (CSVRecord record : records) { allRows++; for (String string : record) { System.out.println(string); if (StringUtils.isBlank(string)) { isValidRow = false; break; } List<String> row = StringTools.splitToListString(string, " “); String readCountWithDescString = row.get(0); String readCountString = StringUtils.substringBefore(readCountWithDescString, “浏览”); String recommendCountWithDescString = row.get(1); String recommendCountString = StringUtils.substringBefore(recommendCountWithDescString, “推荐”); String commentCountWithDescString = row.get(2); String commentCountString = StringUtils.substringBefore(commentCountWithDescString, “评论”); readCount += Integer.parseInt(readCountString); recommendCount += Integer.parseInt(recommendCountString); commentCount += Integer.parseInt(commentCountString); } if (isValidRow) { allValidRows++; } isValidRow = true; } System.out.println(); System.out.println(String.format("[慕课手记] 一共读取%d行,有效行: allValidRows = %d ,其中浏览数: readCount = %d ,推荐数: recommendCount = %d ,评论数: commentCount = %d”, allRows, allValidRows, readCount, recommendCount, commentCount)); System.out.println();}很完美,终于不必再肉眼统计数据了,虽然很长程度上仍然依赖人工整理好 csv 文件,但是目前已经解决了纯手动的弊端.因此,上述解决方案是半手动的方式,仍然还有很多可以优化的地方,等下次忍受不了这种方案时再解决!小结本文主要介绍了纯手工统计报表遇到的诸多问题,寻求一种相对简单的解决方案.基本流程大致可以分为下述流程:手动复制文章列表(包括阅读量,评论量和点赞数),并整理成标准的 csv 格式文件.编写各个平台的 csv 工具处理类,解析并统计 csv 文件内容.运行工具类得到最终统计数据,大功告成!本文主要介绍的是解决问题的思路,对于其中涉及到的相关技术点并未深入展开,关键源码已经贴上,如果还想要更详细的完整源码,可以留言回复. ...

April 11, 2019 · 2 min · jiezi

使用Data URI Scheme优雅的实现前端导出csv

问题描述项目里需要实现一个导出csv的功能,这是个老生常谈的需求,而且我们使用的是iview的组件库,按道理说实现起来应该简单,但实则不然,我在做的时候遇到了一些问题。受限于请求需要token、后端分页、接口、性能等原因不得不放弃iview的导出方式。所以我需要寻找一种可行的、合理的、优雅的导出方案,那就是Data URI Scheme。方案实现方案介绍Data URI Scheme是利用HTML标签的href和src属性来实现的。他看起来像是这样的:<img src=“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==” alt=“Red dot” />或者<a href=“data:text/csv,something”>download</a>按照这种方案的介绍,我们把要导出的数据拼接在href指定位置就能实现导出的需求,代码实现看起来像这样:<a href="" download=“export.csv” id=“export_csv” style=“display=‘none’">download</a>function export_csv (data) { $(’#export_csv’).href = ‘data:attachment/csv,’ + encodeURI(data); $(’#export_csv’).click(); setTimeout(function () { $(’#export_csv’).href = ‘’; })}export_csv(csv_data_str);测试发现,妥妥的,没毛病。存在问题但在实践中这个方案是有限制的:在chrome的实现中这个url最大限制为2MB。所以,当在Chrome下载的文件大小超过2MB chrome便会报这样的错误(其他浏览器这里不做讨论):下载失败-网络错误这里2MB的大小可以在chromium源码中可以看到:const size_t kMaxURLChars = 2 * 1024 * 1024;…if (!iter->ReadString(&s) || s.length() > url::kMaxURLChars) { *p = GURL(); return false;}变量声明部分源码链接变量引用部分源码链接而关于2MB限制的问题在chromium论坛在2010年就被人作为bug提出来了,但是从2010年一直讨论到2019年也没有明显的改善。方案改进chromium不改,那我们只能自己想办法了,于是有大牛提出来使用URL.createObjectURL + Blob来突破这个限制。借助Blob对象和URL.createObjectURL我们可以得到形如下面的URL:blob:https://xxx.com/0bde569d-20a2-4085-95e6-dcec242962c6这样就能突破Chrome对Data URI Scheme URL大小的限制了。当然呢,我们没用过URL.createObjectURL这个方法,也没用过Blob对象,所以我们要看看浏览的支持情况恩,看起来没有问题,那我们来看看代码实现。<a href=”" download=“export.csv” id=“export_csv” style=“display=‘none’">download</a>function export_csv (data) { const BOM = ‘\uFEFF’; let blob_obj = new Blob([BOM + data], {type: ’text/csv’}); let download_url = URL.createObjectURL(blob_obj); $(’#export_csv’).href = download_url; $(’#export_csv’).click(); setTimeout(function () { // 通过createObjectURL创建的url需要通过revokeObjectURL()来释放 URL.revokeObjectURL(download_url); $(’#export_csv’).href = ‘’; })}export_csv(csv_data_str);恩,这样就不怕超过2MB的CSV的导出了,但是Blob对象有大小限制吗?Good question !我们在chromium的说明文档中可以看到一个表:DeviceRamIn-Memory LimitDiskDisk LimitMin Disk AvailabilityCast512 MB102 MB000Android Minimal512 MB5 MB8 GB491 MB10 MBAndroid Fat2 GB20 MB32 GB1.9 GB40 MBCrOS2 GB409 MB8 GB4 GB0.8 GBDesktop 323 GB614 MB500 GB50 GB1.2 GBDesktop 644 GB2 GB500 GB50 GB4 GB从这个表中,大概可以看出来在In-Memory Storage的时候桌面版64位Chrome Blob的上限为2GB(在Chrome 57似乎上限是500MB)。所以从现在看来这种方法应该是安全的。至此,这个问题算是完整的解决了。iview的实现另外,在我写这篇文章的时候我发现iview的export-csv方法也是按照这个方案实施的,而且做了更多兼容,可以方便大家参考。但他在资源释放的地方做的还需改进,也希望大家注意。参考文档Data protocol URL size limitationsExcellent Export and the Chrome URL limitData_URI_schemeexcellentexport pull request无法在nodejs中下载大文件Issue 69227: Loading large URLs kills the rendererIssue 375297: the total blobs’ size cannot exceed about 500MiBIs there any limitation on JavaScript Max Blob sizechromium/url/url_param_traits.cc#L36chromium/url/url_constants.cc#L32iview 3.x export-csv ...

February 17, 2019 · 1 min · jiezi

用最新NLP库Flair做文本分类

摘要: Flair是一个基于PyTorch构建的NLP开发包,它在解决命名实体识别(NER)、部分语音标注(PoS)、语义消歧和文本分类等NLP问题达到了当前的最高水准。它是一个建立在PyTorch之上的NLP框架。本文将介绍如何使用已有的和构建自定义的文本分类器。介绍文本分类是一种监督机器学习方法,用于将句子或文本文档归类为一个或多个已定义好的类别。它是一个被广泛应用的自然语言处理方法,在垃圾邮件过滤、情感分析、新闻稿件分类以及与许多其它业务相关的问题中发挥着重要作用。目前绝大多数最先进的方法都依赖于一种被称为文本嵌入的技术。它将文本转换成高维空间中的数值表示方式。它可以将文档、语句、单词、字符(取决于我们所嵌入的形式)表示为这个高维空间中的一个向量。Flair之所以对NLP来说是一个令人兴奋的消息,是因为Zalando Research最近发表的一篇论文《Contextual String Embeddings for Sequence Labelling(用于序列标注的上下文相关字符串的嵌入)》,其中涵盖了一种始终优于以前最先进方案的方法。该算法在Flair中得到了完整的支持和实现,可以用来构建文本分类器。1、准备安装Flair需要Python 3.6,如果你还没有,请点击这里查看安装向导。然后执行pip命令安装即可:pip install flair上面的命令将安装运行Flair所需的所有依赖包,当然也包括了PyTorch。2、使用一个预训练的分类模型最新的0.4版本包含了两个预训练的模型,还有一个基于IMDB数据集上训练的情感分析模型和“攻击性语言检测”模型(当前仅支持德语)。使用、下载和存储模型都被整合到了一个单一的方法中,这使得使用预训练模型的整个过程都非常简单。要使用情感分析模型,只需执行以下代码片段:fromflair.models import TextClassifierfrom flair.data import Sentenceclassifier = TextClassifier.load(’en-sentiment’)sentence = Sentence(‘Flair is pretty neat!’)classifier.predict(sentence)# print sentence with predicted labelsprint(‘Sentence above is: ‘, sentence.labels)当第一次运行时,Flair将下载情感分析模型,默认情况下将其存储到home目录下的.flair子目录中,这大概得需要几分钟。上面的代码首先加载必需的库,然后将情感分析模型加载到内存中(必要时先进行下载),接下来可以预测句子“Flair is pretty neat!”的情感数值,按0到1的区间赋值。最后命令的输出结果是:The sentence above is: [Positive (1.0)]就是这么简单!例如,现在你可以将上述代码合并在一个REST API中,并提供一个与Google的云自然语言API的情感分析类似的服务,当在有大量请求的生产环境中使用时,这种分析的成本将会非常的高。3、训练一个自定义文本分类器要训练一个自定义的文本分类器,我们首先需要一个标注过的数据集。Flair的分类数据集格式基于Facebook的FastText格式。格式要求在每行的开头定义一个或多个标签,以前缀__label__开头。格式如下:label<class_1><text>label<class_2><text>在本文中,我们将利用Kaggle的Spam Detection数据集通过Flair构建一个垃圾/非垃圾的分类器。这个数据集比较适合我们的学习任务,因为它足够小,并且仅包含5572行的数据,可以在一个CPU上只花几分钟就可以完成一个模型的训练。来自数据集中的标记为spam(垃圾邮件)或ham(非垃圾邮件)的SMS信息3.1 预处理-构建数据集我们首先从Kaggle上的这个链接下载数据集来获得spam.csv文件。然后,在与数据集相同的目录中,我们运行下面的预处理代码片段,该代码将执行一些预处理,并将数据集拆分为训练集、开发集和测试集三个部分。确保安装了Pandas。如果还没有,请先运行命令:pip install pandasimport pandas as pddata = pd.read_csv("./spam.csv", encoding=‘latin-1’).sample(frac=1).drop_duplicates()data = data[[‘v1’, ‘v2’]].rename(columns={“v1”:“label”, “v2”:“text”})data[’label’] = ‘label’ + data[’label’].astype(str)data.iloc[0:int(len(data)*0.8)].to_csv(’train.csv’, sep=’\t’, index = False, header = False)data.iloc[int(len(data)*0.8):int(len(data)*0.9)].to_csv(’test.csv’, sep=’\t’, index = False, header = False)data.iloc[int(len(data)*0.9):].to_csv(‘dev.csv’, sep=’\t’, index = False, header = False);上面的代码将从数据集中删除一些重复数据,并对其进行无序处理(随机化行),并按照80/10/10的比例将数据拆分为训练集、开发集和测试集。如果运行成功,你将会得到FastText格式的三个数据文件:train.csv、test.csv和dev.csv,它们可以与Flair一起使用。3.2 训练自定义文本分类模型请在生成数据集的目录中运行以下代码片段用以训练模型:fromflair.data_fetcher import NLPTaskDataFetcherfrom flair.embeddings import WordEmbeddings, FlairEmbeddings, DocumentLSTMEmbeddingsfrom flair.models import TextClassifierfrom flair.trainers import ModelTrainerfrom pathlib import Pathcorpus = NLPTaskDataFetcher.load_classification_corpus(Path(’./’), test_file=‘test.csv’, dev_file=‘dev.csv’, train_file=‘train.csv’)word_embeddings = [WordEmbeddings(‘glove’), FlairEmbeddings(’news-forward-fast’), FlairEmbeddings(’news-backward-fast’)]document_embeddings = DocumentLSTMEmbeddings(word_embeddings, hidden_size=512, reproject_words=True, reproject_words_dimension=256)classifier = TextClassifier(document_embeddings, label_dictionary=corpus.make_label_dictionary(), multi_label=False)trainer = ModelTrainer(classifier, corpus)trainer.train(’./’, max_epochs=10)第一次运行上述代码时,Flair将会自动下载需要的所有嵌入模型,这可能需要几分钟,然后接下来的整个培训过程还需要大约5分钟。程序首先将所需的库和数据集加载到一个corpus对象中。接下来,我们创建一个嵌入列表,包含两个Flair上下文的字符串嵌入和一个GloVe单词嵌入。然后将此列表作为文档嵌入对象的输入。堆叠和文档嵌入是Flair中最有趣的概念之一,提供了将不同的嵌入整合在一起的方法。你可以同时使用传统的单词嵌入(如GloVe, word2vec, ELMo)和Flair上下文的字符串嵌入。在上面的例子中,我们使用了一种基于LSTM(Long Short-Term Memory,长短期记忆网络)的方法,将单词和上下文的字符串嵌入结合起来以生成文档嵌入。想要了解更多,请点击这里。最后,上述代码进行模型训练并生成了final-model.pt和best-model.pt两个文件,它们表示我们存储的训练模型。3.3 用训练过的模型进行预测我们现在可以从同一目录通过运行以下代码,使用导出的模型来生成预测结果:fromflair.models import TextClassifierfrom flair.data import Sentenceclassifier = TextClassifier.load_from_file(’./best-model.pt’)sentence = Sentence(‘Hi. Yes mum, I will…’)classifier.predict(sentence)print(sentence.labels)这段代码会输出“[ham(1.0)]”,这意味着该模型100%确定我们的示例消息不是垃圾邮件。与其它框架相比表现如何?与Facebook的FastText甚至谷歌的AutoML自然语言平台不同,使用Flair进行文本分类仍然是一项底层的工作。我们可以通过设置诸如学习率、批量大小、退火因子(anneal factor)、损失函数、优化选择等参数来完全控制文本嵌入和训练的方式…为了获得最佳表现,需要调整这些超参数。Flair为我们提供了一个有名的超参数调优库Hyperopt的封装器,我们可以使用它来对超参数进行调优以获得最佳的性能。在本文中,为了简单起见,我们使用了默认的超参数。在大多数默认参数下,我们的Flair模型在10个训练周期后获得了0.973的f1-score。为了进行对比,我们使用FastText和AutoML自然语言平台训练了一个文本分类模型。首先我们使用默认参数运行FastText,并获得了0.883的f1-score,这意味着模型在很大程度上优于FastText。然而,FastText只需要几秒钟的训练时间,而我们训练的Flair模型则需要5分钟。我们将结果与在谷歌的AutoML自然语言平台上获得的结果进行了比较。平台首先需要20分钟来解析数据集。之后,我们开始了训练过程,这几乎花了3个小时完成,但却获得了99.211的f1-score——这比我们自己训练的模型稍微好一点。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 9, 2019 · 1 min · jiezi

用PHP读取Excel、CSV文件

PHP读取excel、csv文件的库有很多,但用的比较多的有: PHPOffice/PHPExcel、PHPOffice/PhpSpreadsheet,现在PHPExcel已经不再维护了,最新的一次提交还是在2017年12月25号,建议直接使用PhpSpreadsheet,而且这两个项目都是同一个组织维护的,本文介绍PhpSpreadsheet的使用。介绍PhpSpreadsheetPhpSpreadsheet这个库是纯PHP写的,提供了非常丰富的类和方法,而且支持很多文件格式:环境要求PHP >= 5.6开启php_zip扩展开启php_xml扩展开启php_gd2扩展开始使用????我们写一个简单的demo,来学习PhpSpreadsheet的使用,大概就是一个简单的文件上传页面,上传我们要读取的Excel文件,PHP接收到文件,调用PhpSpreadsheet读取Excel里面的内容。0. 配置环境略…,自己配置我当前的PHP版本是7.2.131. 新建一个项目mkdir democd demo2. 安装使用composer安装:composer require phpoffice/phpspreadsheet默认安装的是最新的稳定版本(1.5),如果想要安装dev版本,可以执行下面的命令:composer require phpoffice/phpspreadsheet:develop上面步骤执行完毕后,目录结构是这样的:3. 新建一个简单的html文件,用来上传Excel文件vim index.htmlindex.html里面的内容很简单,如下:这里要注意下:form表单的enctype一定要是multipart/form-data这只是一个简单的demo,一个form表单就可以了,运行后就是下面这样了 :)4. PhpSpreadsheet如何使用?在处理前端传过来的Excel文件之前,先来介绍下PhpSpredsheet如何使用。4.1 读取文件PhpSpreadsheet中读取文件有很多种,对于不同格式的文件有不同的读取方法,比如:xlsx格式,使用\PhpOffice\PhpSpreadsheet\Reader\Xlsx(),csv格式,使用\PhpOffice\PhpSpreadsheet\Reader\Csv(),乍一看这么多类就感觉有点复杂,其实这些类都实现了\PhpOffice\PhpSpreadsheet\Reader\IReader和\PhpOffice\PhpSpreadsheet\Writer\IWriter接口,指定了要加载的文件类型。我们可以直接使用\PhpOffice\PhpSpreadsheet\IOFactory这个工厂类:$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load(‘demo.xlsx’);如果想在读写文件的时候设置一些属性,比如读写属性,可以这样设置:$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile(“demo.xlsx”);$reader->setReadDataOnly(true);$reader->load(“demo.xlsx”);使用这个工厂类的好处就是你不需要关心文件上传的格式,它能自动帮识别,其实这个工厂类就是对你上传的文件做一些识别,如果识别出来是xls格式,就返回xls的reader,如果是csv,就返回csv的reader,通过分析代码我们可以看到这个IOFactory可以生产出如下的reader和writer:abstract class IOFactory{ private static $readers = [ ‘Xlsx’ => Reader\Xlsx::class, ‘Xls’ => Reader\Xls::class, ‘Xml’ => Reader\Xml::class, ‘Ods’ => Reader\Ods::class, ‘Slk’ => Reader\Slk::class, ‘Gnumeric’ => Reader\Gnumeric::class, ‘Html’ => Reader\Html::class, ‘Csv’ => Reader\Csv::class, ]; private static $writers = [ ‘Xls’ => Writer\Xls::class, ‘Xlsx’ => Writer\Xlsx::class, ‘Ods’ => Writer\Ods::class, ‘Csv’ => Writer\Csv::class, ‘Html’ => Writer\Html::class, ‘Tcpdf’ => Writer\Pdf\Tcpdf::class, ‘Dompdf’ => Writer\Pdf\Dompdf::class, ‘Mpdf’ => Writer\Pdf\Mpdf::class, ];…可以看到支持的类型还是蛮多的,但是很多都不常用。在IOFactory工厂中还可以指定读写的文件类型,返回对应的reader,这样就免去了识别文件类型的步骤,如下:$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader(“Xlsx”); // 指定为xlsx格式$spreadsheet = $reader->load(“demo.xlsx”);4.2 从源码比较两种读写方式首先,来看下IOFactory这个工厂类,我们在不指定reader类型时直接load,代码内部是要做一个识别格式的操作:// 源码解析// 不指定reader,直接获取上传的文件创建$reader = \PhpOffice\PhpSpreadsheet\IOFactory::load($_FILES[‘file’][’tmp_name’]);// IOFactory::load()public static function load($pFilename){ // 这步棋室就是创建reader,免去了你手动创建 $reader = self::createReaderForFile($pFilename); return $reader->load($pFilename);}// IOFactory::createReaderForFile()// 这步就是返回一个reader,具体返回什么reader,是根据文件名来的public static function createReaderForFile($filename){ // 判断文件是否存在并且可读,会抛出InvalidArgumentException File::assertFile($filename); // 根据文件后缀猜测类型 $guessedReader = self::getReaderTypeFromExtension($filename); if ($guessedReader !== null) { $reader = self::createReader($guessedReader); // Let’s see if we are lucky if (isset($reader) && $reader->canRead($filename)) { return $reader; } } // 如果没有检测到类型,就会遍历默认的reader数组,直到找到可以使用的那个reader foreach (self::$readers as $type => $class) { if ($type !== $guessedReader) { $reader = self::createReader($type); if ($reader->canRead($filename)) { return $reader; } } } throw new Reader\Exception(‘Unable to identify a reader for this file’);}从上面的代码,可以看到在load前是做了文件检测和类型判断的操作,然后再返回对应的reader,接下来,再来看看当我们指定了类型后,做了哪些操作的:// 指定reader$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();$spreadsheet = $reader->load($_FILES[‘file’][’tmp_name’]);上面的就比较简单了,直接创建reader,然后就load了,只是做了一些实例化的操作。这两种方法相比,第二种方法性能更好一点,当然前提是要知道文件格式。5. 读取Excel文件内容让我们接着继续上面的index.html,我们需要编写一个PHP文件来处理请求:require ‘vendor/autoload.php’;$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();try { $spreadsheet = $reader->load($_FILES[‘file’][’tmp_name’]);} catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) { die($e->getMessage());}$sheet = $spreadsheet->getActiveSheet();$res = array();foreach ($sheet->getRowIterator(2) as $row) { $tmp = array(); foreach ($row->getCellIterator() as $cell) { $tmp[] = $cell->getFormattedValue(); } $res[$row->getRowIndex()] = $tmp;}echo json_encode($res);我们先引入autoload,接着创建了一个Xlsx的reader,然后load我们上传的文件,因为在excel中,内容都是按sheet区分的,每一个sheet中都由行和列组成,我们获取到当前使用的sheet,通过sheet获取到行的迭代对象,再针对每一行得到每一列对象,在PhpSpreadsheet中,cell是一个最小的单元,对应着第几行第几列,数据都是存在cell中,得到cell对象我们就能获取到数据。当我们上传如下内容后:返回结果如下:因为我们在读取时,是从第二行开始的,所以第一行的内容就不显示了。这里说一下,在Excel中第三列是一个时间,PhpSpreadsheet对时间的处理有点特殊。在PhpSpreadsheet中date和time在存储时都是作为数字类型,当要区分数字是时间格式时,需要用到format mask,默认情况下,format mask是开启了的,但如果设置setReadDataOnly等于true的话,就不能使用format mask,从而就区分不了数字和时间格式,PhpSpreatsheet将会全部作为数字处理。此时,我们开启只读模式看一下,$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();$reader->setReadDataOnly(true);输出结果如下:第三列就变成了奇怪的数字,当初这个问题还困扰了我半天。5. PhpSpreadsheet读取文件时的一些常用方法如果一个Excel中有多个sheet,只想操作其中的某几个sheet,可以设置setLoadSheetsOnly$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();// 参数支持字符串或一个数组$reader->setLoadSheetsOnly([‘sheet1’,‘sheet3’]);读取指定行和列的数据class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter{ public function readCell($column, $row, $worksheetName = ‘’) { // 只读取A1:E7的数据 if ($row >= 1 && $row <= 7) { if (in_array($column,range(‘A’,‘E’))) { return true; } } return false; }}$myFilter = new MyReadFilter();$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();$reader->setReadFilter($filterSubset);$spreadsheet = $reader->load(‘demo.xlsx’);上面的例子不够通用,可以修改下使之更为通用:class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter{ private $startRow = 0; private $endRow = 0; private $columns = []; public function __construct($startRow, $endRow, $columns) { $this->startRow = $startRow; $this->endRow = $endRow; $this->columns = $columns; } public function readCell($column, $row, $worksheetName = ‘’) { if ($row >= $this->startRow && $row <= $this->endRow) { if (in_array($column,$this->columns)) { return true; } } return false; }}$myFilter = new MyReadFilter(9,15,[‘A’, ‘B’, ‘D’]);列出Excel中所有sheet的名字$reader->listWorksheetNames(‘demo.xlsx’);列出一个sheet的信息,包括多少列、多少行$reader->listWorksheetInfo(‘demo.xlsx’);PhpSpreadsheet的学习与使用就到这,真的很强大,几乎满足了日常的所有需求,是读取Excel、CSV文件的利器。 ...

January 1, 2019 · 2 min · jiezi