有阵子没写MATLAB绘图相关的东西了今天想聊一个特别老、但总有新手来问的话题——图例更多人是搜“lengend”进来的其实就是legend拼写差了个字母问题倒是一样的。先说说这问题的来源。你在MATLAB里画个图加个legend如果曲线名称比较长比如“2024年测试集预测结果对比”默认情况下这个字会横着排图例框被拉得很宽甚至直接冲出坐标区边界配着图里的曲线反而显得很挤。更头疼的是有些工况下想把图例分成两行甚至三行来放一个名字占一行或者一组里几个子项排成网格状这时候就需要专门处理换行。网上问“matlab legend换行”的人很多回答质量参差不齐有的说用TeX的\n有的说直接换单元格其实都对但得看版本和场景。这篇文章我就想把这几种做法彻底捋一遍顺便把相关的图例位置、边框、字体、中文乱码这些坑一并踩掉结合我实际用下来的经验给你一套能直接抄作业的写法。1. 图例换行的几种核心方法先说核心结论MATLAB里面让legend换行并不是一个统一命令能解决的而是要看你怎么构造图例的文本内容以及你希望达到的排版效果。我按照从简单到复杂的顺序把这几年常用的三种思路都拆开讲。1.1 最直白的做法用字符串数组分行输入这一招适合R2019b之后的版本也是我个人最推荐的因为逻辑最简单写起来最不容易出错。正常情况下你写legend的时候可能是这样的plot(1:10, rand(1,10), LineWidth, 1.5); legend(这是第一条曲线的名称);这个写法没问题但是如果你在单引号里直接敲一个回车MATLAB会把字符串当成两行拼接实际上并不会在显示上产生换行效果反而会直接把legend弄成一个一行超高、旁边留白的方块很怪。正确做法是用字符串数组的多个行来作为图例输入。legend这个函数支持直接传一个字符串数组string array或者字符向量元胞数组cell array of char vectors。每一行对应一个图例项多个图例项之间天然是分行显示的根本不需要额外的换行符。figure; plot(1:10, 1:10, r-o, 1:10, (1:10).^2, b-*); legend([这是第一条曲线的名称; 这是第二条曲线的名称很长很长], Location, best);这里用的是[文本1; 文本2]这种写法注意分号在方括号里表示的是纵向拼接出来的效果就是两条曲线分别两个图例项天然分行而且文字长度不受限制图例框会自动适配宽度。如果你用{文本1; 文本2}这种cell格式效果也一样关键就是分号拼接不是逗号拼接。我见过很多人在论坛上问“为什么我的legend全挤在一行”大概率就是害在了这里——用逗号拼接了两个字符串MATLAB当成两个图例项每个图例项对应一条曲线但显示出来还是一个横排。1.2 同一图例项内的强制换行用sprintf和TeX上面讲的字符串数组法解决的是“有多个图例项想让它们竖着排”的问题这个其实用默认的legend就能做到。真正让很多人头大的是一个图例项内部想换行比如一个图例项要写两行文字比如“训练集\n(R²0.985)”或者想做一个组合图例上边是曲线名下边是对应的指标数值这种需求单纯靠字符串数组是做不到的因为一个元素就是一行。这时候就需要在字符串内部插入换行符——也就是newline或者\n。实际用下来直接在legend的字符串里塞\n是不行的比如第一行\n第二行在MATLAB里会把\n当成两个字符原样显示换行没有生效。正确做法是用sprintf来构造字符串figure; x 1:10; plot(x, sin(x), LineWidth, 1.5); legend(sprintf(正弦曲线\n幅值范围[-1, 1]), Location, northeast);sprintf里的\n是真正的换行控制符最终传给legend的其实就是一串带换行的字符。我实际测过这种情况下图例框会变成一个宽度变窄、高度增加的长条换行效果是明确的。但这里有一个非常重要的注意点——TeX解释器下的\n和别处的\n有时候会打架。MATLAB的legend默认Interpreter是tex在这种模式下字符串里的\n不是都能被识别为换行但用sprintf构造出来的换行符是ASCII级别的一个字符不受TeX解释器控制所以能正常显示换行。反过来说如果你想用的其实是TeX命令换行那就要用\\这个转义来写比如legend(第一行 \\break 第二行);\break是TeX的换行命令在legend里是可用的但显示效果跟sprintf(\n)不太一样后者是纯粹的文本换行前者是排版换行对后续文字对齐的影响略有差异。我自己的习惯是如果只是在图例里展示两行文字优先sprintf(\n)最稳、最不容易出幺蛾子。1.3 用Latex解释器实现更复杂的排版换行如果你对图例美观度要求很高比如论文配图里想让图例文字带上公式同时又想多行对齐那上面两种方法都不太够用。这时候就要开Latex解释器。这就涉及到legend的另一个参数Interpreter可以设成latex。开启之后legend里的字符串就会按LaTeX语法解析。LaTeX本身是支持换行的常见的是用\\命令需要amsmath环境或者\newline、\par。但你要注意legend函数默认的Interpreter是tex不是latex直接在里面写LaTeX换行命令可能不生效。所以要么在属性里改要么在构造函数时指定figure; plot(1:10, exp(1:10), LineWidth, 1.5); hLeg legend($e^{x}$\\\\文本第二行, Interpreter, latex);等等这里有个坑——字符串里写LaTeX分隔环境的\\在MATLAB字符串常量里需要转义成\\\\。很多人第一次写的时候会少一层转义结果就是报错或者显示异常。这个真的烦人我的建议是能不手动写多层反斜杠就不写用sprintf来构造也行或者直接先写一个变量再传进去避免视觉混乱txt $e^{x}$ \\\\ 文本第二行; hLeg legend(txt, Interpreter, latex);其实开了Latex解释器之后图例的宽度、对齐方式、字体大小都会更接近正式排版里的效果这对发论文很友好。但它的缺点也明显因为LaTeX语法本身比较复杂一旦字符串里出现中文或者一些特殊字符需要在LaTeX层面处理否则容易出现编译不了或显示空白的问题。我在实际写中文论文的时候一般不会为了换行专门开Latex解释器因为中文字体在LaTeX模式下经常出问题反而是tex模式配合中文字体更容易调通。1.4 方法对比与适用场景为了让你一眼看明白这几种方法的取舍我整理了一个对比表方法适用场景优点缺点推荐指数字符串数组/元胞数组分号构造多个图例项、每条曲线一个名称简单直观版本兼容性好无法在一个图例项内部换行五星sprintf构造带\n的字符串单个图例项内部需要换行换行可靠不受解析器影响需要专门构造字符串稍繁琐五星TeX的\break等命令老版本或特殊排版需求不改变解释器利用既有TeX命令种类少控制力有限四星Latex解释器LaTeX换行论文级排版公式文字多行排版效果最好支持公式中文兼容性差转义麻烦四星这个表是我在实际项目里反复权衡过的。不夸张地说日常90%的需求用前两种就足够了Latex解释器属于锦上添花等你有明确排版要求时再去碰。2. 换行只是开始图例格式的配套细节图例换行这个需求往往不是单独存在的。你既然要换行说明这个图的图例信息量不小那图例的位置、列数、字体、框体格式就得一起考虑否则换行之后可能出现图例框太大、遮挡数据曲线、字体大小与坐标轴不协调等一系列问题。2.1 图例位置和列数怎么设置换了行之后第一个常见问题就是图例框的尺寸变了甚至可能跑出绘图区域。MATLAB里设置图例位置有几种方式最简单的是用Location参数指定大致方位比如north、southeast、best但这只是相对位置框体如果太宽还是会溢出。比如一条曲线名叫“2024年1月-6月光伏发电功率实测数据对比单位kW”如果你不处理图例框宽度几乎要赶上整个坐标区宽度这时候你把Location设成north也没用照样顶到边。对应办法有两个方向第一个方向是手动指定一个归一化位置。图例框的位置和大小可以用Position属性控制四个值分别是[x坐标、y坐标、宽度、高度]取值区间是[0,1]相对坐标区而言。多行换行之后框体高度变大、宽度变小往往需要把框体中心位置往上、往右调整。你可以先设一个大概位置再在Figure窗口里手动拖动图例框拖到满意位置之后查看底层的Position值然后把这个数值固化到脚本里这样导出图片的时候才不会变。第二个方向是设置图例列数。当你的图例项数量不是很多但每个文本比较长的时候与其换行不如考虑分成多列排布。MATLAB里图例是支持多列的通过NumColumns属性来控制hLeg legend({曲线一, 曲线二, 曲线三, 曲线四}, NumColumns, 2);这个参数实测比换行更优雅因为两列矩阵式的排布对空间利用率更高也更好看尤其在英文论文里很常用。但它有个限制如果一个图例项内部的文字特别长分列之后仍然会挤这时候还是得靠换行来解决。2.2 字体、边框、背景的调整细节换行之后图例框高度变大文字行数多了字体的选择就变得重要起来。很多人用MATLAB默认的Helvetica字体中文环境下会变成宋体或者出现乱码这个一会儿单独讲。就换行而言我的经验是如果图例文字包含多行行与行之间的间距可以通过FontSize来间接控制因为MATLAB的图例行距不是独立的属性它是跟字体大小挂钩的。字体越大行距对应变大视觉上就会更稀疏。图例边框线宽和背景颜色也常被忽视。默认情况下图例有个灰色背景和白边如果你把图例拖动到曲线密集区域这个背景框就会遮挡数据。可以用下面这几个属性调hLeg.Box on; % 显示边框 hLeg.LineWidth 0.8; % 边框线宽 hLeg.Color [1 1 1]; % 背景颜色为白色 hLeg.EdgeColor [0 0 0]; % 边框颜色为黑色如果图例被放置到图片空白区域还可以把背景设为透明或者半透明hLeg.Color none; % 背景透明这些设置配合换行之后的框体整体视觉会干净很多。尤其是论文插图一个默认风格的图例框放在彩色曲线旁边效果相当突兀不如花两秒钟调成白底细边框。2.3 图例文字与曲线符号之间的间距这也是一个非常影响美观的细节但容易被忽略。换行后多行图例左边通常会有曲线符号比如实线、虚线、圆圈标记符号之后才是文本。如果符号与文本贴得太近或者离得太远整体视觉都不对。这个间距MATLAB并没有直接暴露一个图例专用的属性但可以手工通过调整ItemTokenSize来改变。这个属性控制的是图例项中标记符号token的显示宽度默认是20像素左右适当调大会让符号和文字之间的留白更舒展hLeg.ItemTokenSize [30, 18];方括号里第一个值是符号区域的宽度第二个是高度方向的大小单位是像素。我给论文配图时经常把这个从20调到30视觉上会舒服很多跟多行排版也更容易搭配。这里再多说一句如果你用了NumColumns且列数较多图例项之间还会出现水平对齐的问题。默认是左对齐如果你希望整体居中可以统一调整图例框的宽度或者把字符串前面补上空格凑出视觉上的居中效果——这招比较土但在赶时间的时候确实有效。3. 实操过程与完整代码示例理论讲了这么多还是得来点落地的东西。我在这里写两个实际场景的完整示例你拿去改一改就能用。3.1 场景一两根长名称曲线各单独一行假设我们在做一个回归模型的拟合效果对比两张子图每张图里有训练集和测试集的曲线曲线名称都特别长。% 示例多子图 多行图例 x 0:0.1:2*pi; figure(Position, [100 100 800 600]); % 第一个子图 subplot(2,1,1); plot(x, sin(x), r-, LineWidth, 1.5); hold on; plot(x, cos(x), b--, LineWidth, 1.5); hold off; legend([训练集正弦函数拟合结果均方误差0.0012; 测试集余弦函数拟合结果均方误差0.0034], ... Location, northeast, FontSize, 9); xlabel(时间 (s)); ylabel(幅值); title(回归模型拟合效果对比); % 第二个子图 subplot(2,1,2); plot(x, sin(x).*cos(x), k-, LineWidth, 1.5); legend(sprintf(混合信号曲线\\n幅值范围[-0.5, 0.5]), ... Location, best, FontSize, 9); xlabel(时间 (s)); ylabel(幅值); title(混合信号细节);这里第一个子图用的是字符串数组分号构造写起来一目了然第二个子图用sprintf来构造第一行和第二行效果是图例项内部换行看起来就像一个图例项分成了上下两层文字。跑出来的效果应该是第一个子图图例框右上角放置两个图例项一上一下排列文字较长但不会撑破坐标区第二个子图图例框在数据比较稀疏的区域单条目双行展示。3.2 场景二自动断行并调整图例框位置有时候你做批量数据处理要一次性生成几十张图每张图的曲线名称都能变化长度也可能不同。这时候如果每张图都手动调图例位置太不现实了。我的办法是写一个自动断行和位置调整的小函数在生成图例后统一处理。function hLeg smartLegend(hAx, names, location) % hAx: 坐标区句柄 % names: 字符串数组或元胞数组 % location: 图例位置如 northwest % 构造图例 hLeg legend(hAx, names, Location, location, FontSize, 9); % 调整图例框位置往右上方微调避免遮挡数据 legPos hLeg.Position; legPos(2) legPos(2) 0.02; % 上移一点点 hLeg.Position legPos; % 设置图例框样式 hLeg.Box on; hLeg.LineWidth 0.6; hLeg.Color w; hLeg.ItemTokenSize [25, 15]; end这个函数虽然简单但批量场景里能省不少事。我一般会在生成图片的循环里直接调用它几十张图跑完图例位置基本都不会出大问题。3.3 保存图像时的图例质量保证换行和图例样式都调好之后导出图片之前还有一个坑——图例显示正常另存为图片之后图例变得模糊或者字体变小。这跟MATLAB的导出分辨率设置有关。用exportgraphics导出时可以指定分辨率图例里的文字和线条都会重绘exportgraphics(gcf, output.png, Resolution, 300);如果你是R2020a之前的版本用的是print函数那就需要指定-r300之类的分辨率参数。图例文字在这种高分辨率导出下会按照实际字体大小重绘不会出现模糊问题。另外提醒一下如果你在Figure窗口里手动把图例拖到了某个位置再用脚本导出图例位置可能会和你看到的不一致。这是MATLAB窗口缩放和实际Figure内部像素之间的偏差导致的。对策就是我之前说的——查看手动调完后图例的Position值把数值写到脚本里固定住位置再导出不要依赖手动拖拽。4. 常见问题与排查技巧实录图例换行看起来是个小功能但实际操作中会冒出一堆奇奇怪怪的问题。下面这几个问题是我在帮别人排查和实际使用中碰到的做个速查表给你。问题现象可能原因解决办法legend里写\n不起作用原样显示直接写的字符串字面量不解析转义符用sprintf或newline构造字符串图例显示成一行超高方块字符串里硬敲了回车或者用了空格删除硬回车改用字符串数组分号或sprintf中文图例显示为方框或乱码字体不支持中文或Interpreter设置导致改字体为宋体、Microsoft YaHei等或关闭TeX解释器换行后图例被截断图例框高度不够或坐标区边界限制手动调整Position或修改Location参数Latex解释器下\\报错反斜杠转义层数不对用sprintf($x$ \\\\ 第二行)构造或改用tex模式图例文字重叠在一起FontSize过小或行数过多增大字体减少行数或分成多列图例框遮挡曲线Location设置不当换位置或手动调Position逐个展开说说我认为值得注意的地方。4.1 中文乱码的坑中文图例乱码是出现频率最高的。这其实不完全是换行引入的问题但换行后需要设置字体时经常触发。MATLAB在Windows系统下默认字体是Helvetica而Helvetica本身不包含中文字形所以图例里的中文会显示成方框。老用户都知道的解决办法是把figure的默认字体改成中文字体set(groot, defaultAxesFontName, 宋体); set(groot, defaultLegendFontName, 宋体);但我实测下来最大的雷往往出在Interpreter设置上。如果你为了换行把Interpreter设成了latex中文基本九死一生因为LaTeX默认不认中文字符除非你额外引入CJK包之类的东西而MATLAB内置的LaTeX环境并不一定支持。所以我一直强调如果你的图例里有中文就别开Latex解释器老老实实用默认的tex模式配合中文字体设置换行照样能做而且不会有乱码风险。另外R2023a之后的版本MATLAB默认字体有所调整中文环境的兼容性好了一些但我还是建议你显式设置一次字体别赌默认值。4.2 特殊字符和转义问题用Tex解释器的时候图例字符串里的_下划线和^上标会被当作排版命令。如果你的曲线名称里正好有下划线比如2024_annual不加处理的话显示出来的可能是“2024”后面带一个奇怪的下标效果。加上换行处理后这种问题更容易被忽视因为你会把注意力放在换行符上忘了处理特殊字符。解决办法是在下划线前面加一个反斜杠写成2024\_annual。如果你有多个特殊字符要处理可以用strrep批量替换name strrep(name, _, \_);还有百分号%在sprintf的格式串里会被当作格式化占位符如果你构造字符串时正好用了sprintf里边的%必须写成%%才能正常显示。这个坑非常隐蔽我见过不止一次图例里显示“100%”结果变成了“100”或者直接报错。4.3 与其他工具场景的对比思考你可能看到网络热词里还有“markdown换行”“echarts tooltip自动换行”“gis图例标签如何换行”这些搜索流其实它们属于同一类需求在不同工具里让文字按列排布、按行截断。MATLAB的图例换行本质上也是“容器内多行文本的排版问题”。我拿前端领域来类比echarts的tooltip里文字换行通常用HTML标签br/这跟MATLAB里sprintf(\n)的思想是一样的——都是插入一个分行控制符只是语法形式不同。而GIS图例标签换行比如ArcGIS里图例项文字很长时自动换行往往需要在图例属性里开启“自动换行”开关。MATLAB没有这个自动换行开关必须手动指定在哪断开这一点确实麻烦但也换来了精确控制的好处。理解了这个共通逻辑你就不容易在各种工具之间来回蒙圈了。5. 版本兼容性与不同场景的补充最后聊聊不同版本和不同使用场景下的补充细节因为图例换行这种行为在不同版本里真的会不一样。5.1 R2019b前后版本的差异字符串数组的[a; b]写法是R2019b及以后才有比较完整的支持。如果你的实验室电脑还装的是R2018a或者更早的版本这种写法可能会直接报错。老版本稳妥的办法是用两个字符向量拼接成一个元胞数组legend({第一项; 第二项}, Location, best);这个写法从R2006a一直到最新版都兼容是做项目时最保险的写法。如果你的代码将来要在不同机器上跑别人机器上装的老版本MATLAB也能跑那建议还是采用元胞数组的写法别贪图新语法。sprintf(\n)换行这个方法老版本同样适用因为sprintf和TeX解释器从很早版本就有不存在兼容性障碍这也是我推荐它作为换行首选方案的原因之一。5.2 热词里那些“MATLAB安装”“中文注释乱码”的延伸这次的网络热词里出现了一堆“matlab安装”“matlab下载”“matlab 2025导出eps”之类的关键词和legend换行一起涌入我很能理解——很多人其实是刚装了MATLAB正在学画图画着画着就遇到了图例问题。如果你是新手装好环境后第一天就想复现效果那我建议先跑一下最简单的版本plot(rand(10, 2)); legend({随机数据A; 随机数据B});只要能出图、能出图例说明基本环境是通的。如果这个最简单的都报错那大概率不是图例写法的问题而是安装不完整或者路径没配好优先去检查MATLAB的Toolbox路径和许可证。至于中文注释乱码跟图例乱码是同一个根因——字体问题。我的建议是顺手把启动时默认字体设置好一劳永逸。打开startup.m在里面加上几行默认字体和默认解释器配置之后每次启动MATLAB就自动生效set(0, DefaultAxesFontName, Microsoft YaHei); set(0, DefaultTextFontName, Microsoft YaHei); set(0, DefaultLegendFontName, Microsoft YaHei);这样处理之后无论是图例还是坐标轴注释中文基本不再乱码。5.3 视频脚本和函数封装经验写到这里想到一个使用场景可能对一部分读者有用如果你的MATLAB脚本要被别人反复调用或者你要把自己的绘图工具封装成函数图例文字的构建最好做成参数传递不要把图例字符串硬编码在函数内部。因为一旦硬编码换行方式和其他人需要显示的内容就可能冲突。比如我封装过一个绘图函数其中一个参数是labels类型是字符串数组函数内部直接这样调hLeg legend(labels, Location, best);由于labels本身是多行字符串数组天然就是多行图例。外部调用者不需要关心内部怎么换行只需要按照字符串数组的格式传进来就行。这样的封装既保留了灵活性又把换行机制的复杂度隐藏起来了是最推荐的一种工程实践。6. 写到最后的一些实操体会我正在整理这篇内容的时候顺手把上个月画水文预测结果图时的代码翻了出来。当时也是被图例折腾了半小时因为曲线名里有下标、有中文、还要求图例放在空白区域不能挡曲线最后结合字符串数组分行和手动调整Position才解决。回想起来图例换行根本不需要背什么复杂的语法核心就记住两件事多图例项要分行用分号拼接单图例项内部分行用sprintf(\n)。剩下的边框、位置、字体都是锦上添花。每个人的使用场景不一样但如果你在项目中遇到图例文字太长、显示重叠、或者因为图例盖住了曲线数据而苦恼希望这篇整理能帮你少走几步弯路。下次画图的时候多试几次不同的换行组合你会慢慢找到最适合自己图表风格的排版方式。
