GDI+下字体大小自适应方案初探

在某个瞬间,我忽然发觉,三体或是AI,本质上是非常相近的事物,甚至在面对任何未知领域的时候,人类总会不自觉地划分为降临派、拯救派和幸存派。姑且不论马斯克等人叫停 GPT-5 的真实动机如何,当大语言模型(LLM)裹挟着 AIGC 的浪潮气势汹汹地袭来时,你是否会像很多人一样,担心有一天会被机器取代以致于失业呢?此前,我曾自嘲般地提到过,我是一名 YAML 工程师 、Markdown 工程师、Dockerfile 工程师……,甚至以后还会变成一名 Prompt 工程师,而这背后的因果关系,本质上我们对这个世界的编程方式,正在逐步地从 DSL 转向自然语言。我个人认为,任何低端、重复的工作最终都会被机器取代,而诸如情感、艺术、心理、创意……等非理性领域,则可能会成为人类最后的防线。两年前,柯洁以 0:3 的比分输给 AlphaGo,一度在棋盘前情绪失控,我想,那一刻他大概不会想到两年后还会出现 ChatGPT。在《蜘蛛侠:英雄无归》 电影里面,彼得·帕克对奇异博士说,“你知道比魔法更神奇的东西是什么吗?是数学”。我个人非常喜欢这句话,因为在绝对的理性面前,一切的技巧都是徒然,更重要的是,如此深刻的哲理,居然是来自生活中一个真实案例。

电子签章与数学

好的,虽然我们说那些低端、重复的工作最终都会被机器取代,但是真正残酷的现实是,我们并没有那么多需要创造力的工作,就像我们并不需要那么多架构师一样。毕竟,你想象不到,一个人在五年前和五年后做的工作毫无差别,特别是企业级应用中非常普遍的打印。过去这些年,企业数字化转型的口号一直在喊,可到头来我们并没有等来真正的无纸化,企业依然对打印单据这件事情乐此不疲,仿佛没有这一张纸业务就没法开展一样。在这个过程中,企业会希望你能在单据上加盖公司的印章,这就产生了所谓的“电子签章”的需求。当然,我们这里不考虑电子签章的申请、加/解密、防伪等实际的流程,我们只是考虑将其通过 GDI+ 绘制出来即可。考虑到印章有圆形和椭圆形两种形制,所以,我们下面来进行分类讨论。

圆形印章

可以注意到,圆形印章通常由四部分组成,分别是顶部文字、中心部分的五角星、中下部分文字和底部文字。

在这里插入图片描述

其中,顶部文字表示印章所属的公司/组织/机构,底部文字表示14位印章编号,这两部分文字均呈圆弧状分布。具体该如何实现呢?我们来一起看一下。首先,圆形印章的轮廓是一个标准的圆形,这个绘制非常容易:

// 从位图创建一个画布
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
var g = Graphics.FromImage(bitmap);// 绘制圆形边框 
var rect = new RectangleF(x, y, radius, radius);
var Pen pen = new Pen(Color.Red, 3.0f);
g.DrawEllipse(pen, rect);

而对于中心部分的五角星,我们使用一个路径填充即可。此时,问题的关键是在圆上找出五角星的五个顶点。显然,五角星的顶点满足下面的几何关系:

在这里插入图片描述

利用三角函数的知识,我们可以非常容易地写出对应代码,请注意,计算机中使用的坐标系 Y 轴正方向向下:

var Radius = rect.Width / 2 * 0.45;
var Center = new PointF(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
PointF[] points = new PointF[]
{// P0new PointF(Center.X, (float)(Center.Y - Radius)),// P1new PointF((float)(Center.X + Radius * Math.Sin(72 * Math.PI / 180)), (float)(Center.Y - Radius * Math.Cos(72 * Math.PI / 180))),// P2new PointF((float)(Center.X + Radius * Math.Sin(36 * Math.PI / 180)), (float)(Center.Y + Radius * Math.Cos(36* Math.PI / 180))),// P3new PointF((float)(Center.X - Radius * Math.Sin(36 * Math.PI / 180)),(float)( Center.Y + Radius * Math.Cos(36 * Math.PI / 180))),// P4new PointF((float)(Center.X - Radius * Math.Sin(72 * Math.PI / 180)), (float)(Center.Y - Radius * Math.Cos(72 * Math.PI / 180))),
};// 根据五个点生成一个封闭路径
var path = new GraphicsPath(FillMode.Winding);
path.AddLine(points[0], points[2]);
path.AddLine(points[2], points[4]);
path.AddLine(points[4], points[1]);
path.AddLine(points[1], points[3]);
path.AddLine(points[3], points[0]);
path.CloseFigure();// 填充路径
g.RotateTransform(0);
g.FillPath(new SolidBrush(Color.Red), path);

接下来,我们来考虑如何绘制这两段呈圆弧状分布的文字,这里需要用到的数学知识是圆的参数方程以及三角函数。其基本思路是:选定一个起始角度,再根据总角度和文字数目计算一个步长,进而确定每一个文字对应的角度。譬如,这里假定上半部分圆弧总角度为300 度,下半部分圆弧总角度为 60 度:

var center = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
var fontToFit = new Font("宋体", 13, FontStyle.Bold, GraphicsUnit.Pixel);
var totalAngle = Math.PI * 5 / 3;
var stepAngle = totalAngle / (text1.Length + 1);
var startAngle = Math.PI * 4 / 3;
for (int i = 0; i < text.Length; i++)
{float angle = (float)(startAngle - (i + 1) * stepAngle);if (angle < 0) angle += (float)Math.PI * 2;var point = new PointF(center.X + radius * (float)Math.Cos(angle), center.Y - radius * (float)Math.Sin(angle));g.DrawString(text1[i].ToString(), fontToFit1, brush, point.x, point.y);
}

我们知道,在三角函数定义中,逆时针方向为正方向,所以,对于上半部分的弧形文字,只需要用起始角度依次减去对应的步长数。为了让后续计算角度的时候更方便一点,这里会将负角统一转换为正角。接下来的事情就顺理成章啦,因为你可以利用三角函数计算出对应的坐标,此时,我们只需要在指定的位置调用 DrawString 函数将每个字符绘制出来即可:

在这里插入图片描述

不过,你很快会发现一个问题,那就是这些文字的方向并没有像一般印章那样,始终“正对”着你的实现。此时,你会用到第三个数学知识,即:当一个点变为原点以后,它与 X 轴正方向的夹角会如何变化?你为什么需要这个知识呢?因为我们需要对每个字符做一次平移变换、一次旋转变换,这样才能达到我们的目的,即:无论你从哪一个方向去看这些文字,它对你来说都是“正”的,其代码实现如下:

var center = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
var fontToFit = new Font("宋体", 13, FontStyle.Bold, GraphicsUnit.Pixel);
var totalAngle = Math.PI * 5 / 3;
var stepAngle = totalAngle / (text1.Length + 1);
var startAngle = Math.PI * 4 / 3;
for (int i = 0; i < text.Length; i++)
{float angle = (float)(startAngle - (i + 1) * stepAngle);if (angle < 0) angle += (float)Math.PI * 2;var point = new PointF(center.X + radius * (float)Math.Cos(angle), center.Y - radius * (float)Math.Sin(angle));g.TranslateTransform(point.X, point.Y);var transformAngle = (float)(angle * 180 / Math.PI + 90);if (transformAngle > 360) transformAngle -= 360;// 注意:RotateTransform() 方法旋转方向是顺时针方向,所以,要用 360 度减去当前角度// 印章上方的文字需要正对着外侧,所以,要再加上 180 度transformAngle = 360 - transformAngle + 180;g.RotateTransform(transformAngle);g.DrawString(text[i].ToString(), fontToFit, brush, 0, 0, format);g.ResetTransform();
}

这里的关键是 TranslateTransform()RotateTransform() 两个方法,这就是我们上文中提到的平移变换和旋转变换。为什么要这样处理呢?因为我们希望的看到是,文字旋转到“正确”的方向且保持位置不变,如果没有平移转换的话,它会就变成点 A 围绕 点 B 旋转,这样显示不符合我们的预期。总之,当一切都在向着预期的方向发展的时候,我们就可以利用这个技巧“照葫芦画瓢”,需要注意的是,底部弧形文字的“正方向”是向下的,因此,在做旋转变换的时候,两者会相差 180 度。对此,我想说,数学真的好用:

在这里插入图片描述

相比之下,绘制中下部分文字就非常简单啦,因为它是在一个矩形范围上绘制,你唯一需要的就只有一个勾股定理。
理论上,你只要将以上代码片段整合起来,就可以绘制出一个相对完美的印章,我个人踩坑的体会是:真正的困难常常是印章的实际尺寸、打印尺寸、DPI、分辨率这些客观因素。

椭圆形印章

好了,接下来再说椭圆形印章的绘制,原理基本相同,对应的数学知识是椭圆的参数方程。不知道大家是否还记得圆锥曲线、离心率、焦点等概念,博主在写这篇博客的时候,的确是回过头再次重温了这些知识。作为一名程序员,平时没少被别人“教育”业务的重要性,可对我来说,业务大概就是朝三暮四、朝秦暮楚的代名词,相比这些人为想象和构筑的东西,我更喜欢风、沙、星辰这些接近自然和宇宙的东西,对我来说,数学便是如此。好了,下面是绘制弧形文字的代码片段,供大家参考:

var a = rect.Width / 2 * 0.8f;
var b = rect.Height / 2 * 0.8f;
var center = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
var fontToFit = new Font("宋体", 13, FontStyle.Bold, GraphicsUnit.Pixel);
var totalAngle = Math.PI * 5 / 3
var stepAngle = totalAngle / (text1.Length + 1);
var startAngle = Math.PI * 4 / 3
for (int i = 0; i < text.Length; i++)
{float angle = (float)(startAngle - (i + 1) * stepAngle);if (angle < 0) angle += (float)Math.PI * 2;// 利用椭圆参数方程计算坐标var point = new PointF(center.X + a * (float)Math.Cos(angle), center.Y - b * (float)Math.Sin(angle));g.TranslateTransform(point.X, point.Y);var transformAngle = (float)(angle * 180 / Math.PI + 90);if (transformAngle > 360) transformAngle -= 360;// 注意:RotateTransform() 方法旋转方向是顺时针方向,所以,要用 360 度减去当前角度// 印章上方的文字需要正对着外侧,所以,要再加上 180 度transformAngle = 360 - transformAngle + 180;g.RotateTransform(transformAngle);g.DrawString(text[i].ToString(), fontToFit1, brush, 0, 0, format);g.ResetTransform();
}

可以看出,本质上只需要用半长轴 a 和半短轴 b 替换半径即可。考虑到圆是椭圆的特殊形式,这种从特殊到一般的认知方式,难道不显得有趣吗?同样地,这里提供一个椭圆形印章的效果图:

在这里插入图片描述

至此,关于如何通过 GDI+ 绘制印章,笔者可谓是倾囊相授啦,在这个过程中,我最享受的环节,恰恰是那些再寻常不过的数学知识,当你觉得 AI 有一天一定会取代人类的时候,我以为,这代表着绝对理性的胜利,就像令柯洁落泪的 AlphaGo 一样,没有任何技巧,它只是在经历无数次计算以后得出的必然结果,从某种意义上来讲,数学反而是宇宙间最简单的学问,不是吗?如果说人工智能里还有哪些更接近“玄学”的东西,我以为,大概是我们自始至终都没能解释清楚,模仿神经元的神经网络到底是怎样一步步地产生了“意识”?而这一切就好比,你的确知道 ChatGPT 给了你一个满意的答案,但你始终不知道的是,它到底是在经过了一个什么样的思考过程以后,才能给出一个如此契合你心理预期的答案?恍惚之间,我会觉得它符合人类眼中的“高情商”标准,即使它对你一无所知,可它还是能讲出令你“舒服”的话语,如果语言本身就充满了这种迷惑性,那么人类最看重的情感到底又算什么?

字体自适应方案

好了,现在让我们来考虑得更长远一点,当我们实现了一个相对通用的印章绘制算法以后,我们会希望通过配置这些文字来生成不同的印章。此时,一个新的问题产生了:在印章尺寸固定(有相关标准)的情况下,如何能兼容不同长度的文字?一个容易想到的方案是修改字体大小。譬如,当文字较多的时候就缩小字体,当文字较少时就放大字体。所以,下面我想分享的是动态调整字体大小实现字体自适应。

基于宽高

第一种方案基于宽高,即字体是绘制在一个矩形区域内,印章的中下部分通常用来表示印章的用途,此时,我们可以利用 MeasureString 这个方法来测量整个字符串的宽度,并将其和当前矩形的宽度进行比较。如果实际宽度大于当前矩形的宽度,则需要减小字号;如果实际宽度小于当前矩形的宽度,则需要增加字号。当然,缩小的时候可以给一个最小字号,因为你要确保别人能看清印章上的文字;放大的时候需要考虑矩形的高度,因为你要确保印章上的元素不会相互重叠。下面是一个基本实现:

float ScaleFontSizeByContainerSize(Graphics g, string text, Font font, SizeF size) {var fontSize = font.Size;// 对字体缩小时需要考虑最小的字体大小var measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));while (measuredSize.Width > size.Width) {fontSize -= 0.5f;if (fontSize <= MIN_FONT_SIZE) break;measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));}// 对字体放大时需要考虑高度的问题measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));while (measuredSize.Width < size.Width && measuredSize.Height < size.Height) {fontSize += 0.5f;measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));}return fontSize;
}

如图所示,下图展示了相同尺寸下,文字根据字数的多少按不同的字号动态进行缩放:

在这里插入图片描述

基于周长

第二种方案基于周长,主要是针对印章中呈圆弧状分布的这些文字,此时,宽度和高度不足以评估文字能否“恰当”地分布在印章上,所以,我们就可以尝试用周长来进行比较。按照微积分的思想,我们可以粗略地认为,每个字符的宽度累加起来,其总和就约等于对应的这段孤线的长度。在这种情况下,我们可以考虑使用弧长公式和椭圆的周长公式。当文字宽度小于周长时,表明字体还可以放大一点;当文字宽度大于周长时,表明字体还可以缩小一点。类似地,这里给出一个基本实现:

float ScaleFontSizeByPerimeter(Graphics g, string text, Font font, float radius, float angle) {var fontSize = font.Size;// 圆形周长公式var perimeter = angle * Math.PI * radius / 180;// // 椭圆周长公式// var h = Math.Pow((a - b) / (a + b), 2);// var c = Math.PI * (a + b) * (1 + (3 * h / (10 + (4 - 3 * h))));// var perimeter = c * angle / 360;// 对字体缩小时需要考虑最小的字体大小var measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));while (measuredSize.Width > perimeter) {fontSize -= 0.1f;if (fontSize <= MIN_FONT_SIZE) break;measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));}// 对字体放大时需要考虑高度的问题measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));while (measuredSize.Width < perimeter) {fontSize += 0.1f;if (fontSize >= MAX_FONT_SIZE) break;measuredSize = g.MeasureString(text, new Font(font.FontFamily, fontSize));}return fontSize;
}

如图所示,下图展示了相同尺寸下,文字根据字数的多少按不同的字号动态进行缩放:

在这里插入图片描述

本文小结

写这篇博客时,其实是在一个多事之秋。如果是指新海诚的新电影《铃芽之旅》,我大概会写遗憾以及自我和解;如果是指 ChatGPT,我大概会写我对于人工智能的看法;如果是指景甜和张继科这对分手了的男女朋友,我大概会写我对于人类情感的理解;如果是指中国电科的离职事件,我大概会写我对生产力/生产关系的想法……可当很多东西纠缠在一起的时候,任何一种情绪注定都无法独自存活下去,所以,我还是决定写一点可以掌控的事物。如彼得·帕克所言,“比魔法更神奇的东西是数学”,在一个不确定性远大于确定性的时代,能够再次感受到数学世界的美好实在是一种幸运,因为一切的公式/定理都会引导你通往某个确定的地方,正如当我画完圆形印章以后,我可以快速地画出椭圆形印章,它不会增加一丝一毫的心智上的负担,而人类热衷于构建的业务流程,则永远都存在着这样那样的缺陷。也许,直到这一刻,我才能够明白科学家们渴望用一个公式描述这个世界的偏执。因为,在一个不确定的世界里寻找确定,这件事情本身就足够迷人,就像你输入到 AI 模型里的一个个提示词,它们本身或许是随机的、不确定的,可你需要的可能是一个确定的结果。毕竟,这个世界早就复杂到连选择本身都是一件困难重重的事情,对吧?
再次感受到数学世界的美好实在是一种幸运,因为一切的公式/定理都会引导你通往某个确定的地方,正如当我画完圆形印章以后,我可以快速地画出椭圆形印章,它不会增加一丝一毫的心智上的负担,而人类热衷于构建的业务流程,则永远都存在着这样那样的缺陷。也许,直到这一刻,我才能够明白科学家们渴望用一个公式描述这个世界的偏执。因为,在一个不确定的世界里寻找确定,这件事情本身就足够迷人,就像你输入到 AI 模型里的一个个提示词,它们本身或许是随机的、不确定的,可你需要的可能是一个确定的结果。毕竟,这个世界早就复杂到连选择本身都是一件困难重重的事情,对吧?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/28803.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超实用攻略!GPT能玩的这么6,你居然还不知道?

开篇 自古以来,智者皆知学无止境,而在我们身边,正有一款奠基于这个原则的AI机器人—ChatGPT,他擅长从网络上学习各种知识,然后把这些知识用在他的对话中。没错,它就是天马行空的闲话家,无所不谈的取经者。可你知道怎样让它更加符合你的使用需求,适应你的工作节奏么?哦…

基于本地知识库的问答机器人langchain-ChatGLM

原文&#xff1a;基于本地知识的问答机器人langchain-ChatGLM - 知乎 背景 ChatGPT火了后&#xff0c;各种大语言模型&#xff08;LLM&#xff09;模型相继被发布&#xff0c;完全开源的有ChatGLM、BLOOM、LLaMA等。但是这些模型学到的知识是滞后的&#xff08;比如ChatGPT的…

诞生的新职业——提示工程师,年薪已经达到了25万-33万美元

提示工程&#xff0c;可以说是玩转ChatGPT、DALLE 2等等这类AI模型的「必修课」。 但这个「提示」&#xff08;prompt&#xff09;具体要怎么写&#xff0c;多少都有些玄学在里面…… 也难怪由此诞生的新职业——提示工程师&#xff0c;年薪已经达到了25万-33万美元。 就在前不…

安全运营场景下的语言模型应用

接上篇&#xff0c;将安全运营的定义为“使用算法能力提取关键信息”&#xff0c;以此来规避算法误判漏判带来的责任问题&#xff0c;同时提升运营人员的工作效率。在这篇尝试对语言模型的使用方法做一下讨论和分享。 1. 语言模型 先聊一下语言模型。&#xff08;这里刻意规避…

聚观早报 | ChatGPT 停止 Plus 付费;李子柒油管广告收益登顶热搜

今日要闻&#xff1a;ChatGPT 停止 Plus 付费&#xff1b;李子柒油管广告收益登顶热搜&#xff1b;亚马逊游戏部门百名员工被裁&#xff1b;国内一公司推出太空葬&#xff1b;苹果将在印度国金融中心开设零售店 ChatGPT 停止 Plus 付费 4 月 5 日消息&#xff0c;ChatGPT 目前…

chatgpt赋能python:Python读取CSV:简单易懂的教程

Python读取CSV&#xff1a;简单易懂的教程 Python是一种功能强大的编程语言&#xff0c;它可以处理各种不同类型的数据。当需要处理大量的数据时&#xff0c;CSV文件就是一种非常方便的处理方式。这篇文章将介绍如何使用Python来读取CSV文件&#xff0c;帮助您更高效地进行数据…

chatgpt赋能python:Python如何选取CSV某几列数据

Python如何选取CSV某几列数据 在数据处理过程中&#xff0c;CSV是一种非常常见的数据文件类型。CSV文件中的数据由逗号分隔的值&#xff08;Comma-Separated Values&#xff09;组成。处理CSV数据的任务之一是从CSV文件中选择特定的列数据&#xff0c;以进行数据分析或处理。在…

chatgpt赋能python:Python实现CSV文件只取某两列的方法详解

Python实现CSV文件只取某两列的方法详解 介绍 CSV是一种常见的数据格式&#xff0c;通常使用逗号或分号分隔不同的字段。在处理CSV文件时&#xff0c;我们经常需要只提取其中的某些列&#xff0c;以便进行进一步的分析或处理。使用Python语言&#xff0c;可以很方便地实现这一…

postman读取csv文件

postman读取csv文件 &#xff08;1&#xff09;创建登录接口&#xff0c;传入用户名和密码 (2)创建读取的csv文件 &#xff08;3&#xff09;运行脚本读取csv文件内容 &#xff08;4&#xff09;读取csv文件结果 备注&#xff1a; 1.需要将csv文件转换为utf-8编码格式的…

.csv文件与数据的读取

.csv文件的介绍 .csv文件&#xff08;comma seperated value:逗号分隔值&#xff09;&#xff0c;是一种常见的用来存储一维或者二维的数据文件&#xff0c;它使用换行符与逗号存储数据&#xff0c;实际上是一种txt文件。 如图&#xff0c;左侧为原二维数据表格&#xff0c;右…

csv文件读取

csv文件读取 文章目录 csv文件读取第一种方式&#xff1a;列表第二种方式&#xff1a;字典 CSV文件写入第一种方式&#xff1a;列表第二种方式&#xff1a;字典 第一种方式&#xff1a;列表 示例&#xff1a; import csv with open("stock.csv",r,encodingGBK) as …

chatgpt赋能python:Python如何导入CSV的完全指南

Python如何导入CSV的完全指南 CSV是一种常见的数据格式&#xff0c;在数据分析和处理中使用广泛。使用Python&#xff0c;我们可以轻松地读取、处理和分析CSV文件。在本指南中&#xff0c;我们将介绍如何使用Python导入CSV文件。 什么是CSV文件&#xff1f; CSV文件是按照逗…

chatgpt赋能python:Python如何保存数据到CSV文件中

Python如何保存数据到CSV文件中 作为一门广泛应用于数据分析和机器学习的编程语言&#xff0c;Python提供了许多方法来处理和保存数据。其中之一是将数据保存到CSV文件中。本篇文章将介绍如何使用Python保存数据到CSV文件&#xff0c;在此过程中&#xff0c;我们会提到一些有用…

读取csv数据结果包含“\t”的问题

1、实例 1、这个是我手动创建的csv文档&#xff0c;内容是从Word复制粘贴到表格 2、这是我读取数据用的代码 import csvfile open(r"C:\Users\Administrator\Desktop\cs1.csv","r",errors"ignore",encoding"utf-8") csv_file csv…

【使用ChatGPT自动化】批量转换.csv文件为.xlsx文件

第1次提问&#xff1a; 我&#xff1a;我想使用Python批量转换.csv文件为.xlsx文件&#xff0c;请你提供代码 它&#xff1a;好的&#xff0c;以下是使用Python批量转换.csv文件为.xlsx文件的代码&#xff1a; import os import glob import pandas as pddef csv_to_xlsx(pa…

chatgpt赋能python:Python加速读取CSV文件的方法

Python加速读取CSV文件的方法 介绍 CSV文件是一种常见的数据格式&#xff0c;因为其简单和易于理解&#xff0c;被广泛应用于数据处理和数据分析。然而&#xff0c;在处理大型CSV文件时&#xff0c;读取速度会成为问题。Python作为一种高级编程语言&#xff0c;具有易学易用的…

C# 读取CSV文件

上一篇写到了读取文本文件的各种方式&#xff0c;这次我们来读取一下CSV文件&#xff1b; CSV文件在日常工作中用到的也很多&#xff0c;很多时候我们导出大量表格数据的时候&#xff0c;EXCEL无法存储&#xff0c;CSV就是很不错的选择&#xff1b; CSV文件其实也是一种文本文…

C++读取CSV文件中的数据

CSV文件是一种文本文件&#xff0c;表示的是Excel表格数据&#xff0c;可以由办公软件Excel轻松生成。为了在程序中使用Excel数据&#xff0c;就需要以文本的形式操作Excel数据&#xff0c;具体就是操作CSV表格数据。如下所示&#xff0c; #include <iostream> #include …

chatgpt赋能python:Python转CSV:一种简便的数据格式转换方法

Python 转 CSV&#xff1a;一种简便的数据格式转换方法 在本文中&#xff0c;我们将介绍如何使用 Python 将数据从其他数据格式转换为 CSV 格式。CSV 是一种广泛使用的数据格式&#xff0c;它非常适合用于存储大规模数据&#xff0c;并能够被许多应用程序和编程语言读取和处理…

30、【backtrader股票策略】《151 trading strategies》中的支撑与阻力策略(support and resistance)

在《151 trading strategies》中的3.14节,提到了一个关于支撑与阻力的策略,在本策略中,将尝试在全A股中进行测试这个策略,本节主要包含四个部分: 策略逻辑的说明策略实现代码策略测试结果策略绩效的简单分析策略逻辑 我们使用全市场的A股日数据进行测试,只做多头。 资金…