工作上遇到了一个Excel中换行的问题,就是使用了Excel的默认自动换行后,如果一个单词很长,那么一般情况下是不会在单词中间换行的。在网上查了些资料,最终找到了一个不算太完美的方法。

结果

就是使用vba修改单元格里面的内容,根据单元格的宽度,以及字体的字号大小来粗略的估算一行可以放多少个字,然后插入硬换行符,vba里面是chr(10)+chr(13),大概的效果如下:
示例中的文字是:这里是 :big computerserviceimpl是我们有的时候也不没干鲜果品轜羁 and the services are all avalible




可以看到最终的效果确实是有些差强人意,明明感觉还是可以再放一个字符的
以下是例子中使用的代码:

'允许在西文单词中间换行,插入换行符
Sub formatCellString()Dim c As ObjectDim fontName As String, fontSize As Integer, defaultFontSize As IntegerDim str As String, count As Integer, limit As Integer'在这里指定要格式化的是哪个单元格Set c = Worksheets("Sheet2").Cells(1, 1)fontName = c.font.NamefontSize = c.font.SIZEDim resultString As String'取Sheet1中第一行第1023列这个单元格的字体大小作为默认字体大小'需要注意的是,如果实际业务中这个单元格的字体大小也被手动修改了的话,就需要寻找其他尚未被修改过字体及字号的单元格了defaultFontSize = Worksheets("Sheet1").Cells(1, 1023).font.SIZE'先获取单元格的宽度,单元格宽度是在字体默认大小下的limit = WorksheetFunction.Ceiling(c.ColumnWidth * defaultFontSize / fontSize, 1)'要进行格式化的字符串str = c.ValueFor i = 1 To Len(str)Dim cur As Integer, curStr As String'当前字符curStr = Mid(str, i, 1)'取得当前字符的长度,中文算2个,其他算1个If StrWithChinese(curStr) Thencur = 2Elsecur = 1End If'这里直接写了个2是因为有时候中英文一起的计算当前行已有宽度时会有个正好相等的问题If (count + 2) >= limit Then'如果再加上当前字符后,当前行长度就等于单元格宽度了,则插入一个换行符resultString = resultString & Chr(10) & Chr(13) & curStr' count从头开始统计count = 1'MsgBox ("restart count:" & count & " limit:" & limit & " cur:" & cur & " curStr:" & Mid(str, i, 1) & " result:" & resultString)Else'统计当前行字符数count = count + cur'拼接结果字符resultString = resultString & curStr'MsgBox ("continue count:" & count & " limit:" & limit & " cur:" & cur & " curStr:" & Mid(str, i, 1) & " result:" & resultString)End IfNext'c.Value = resultString '实际使用时可取消该行注释Worksheets("Sheet2").Cells(2, 1) = resultString '实际使用时可注释掉该行
End Sub'判断是否包含中文字符
'源码引自:http://www.office-cn.net/excel-vba/981.html
Function StrWithChinese(StrChk As String) As BooleanStrChk = VBA.StrConv(StrChk, vbNarrow)StrWithChinese = IIf(Len(StrChk) < LenB(StrConv(StrChk, vbFromUnicode)), True, False)
End Function

当然了,这个代码是把sheet2里面A1的内容插入好换行符后写入到了A2单元格,只是为了好对比效果,如果是实际使用的话,可以根据注释修改一下最后的赋值语句即可。

小结

我以为到这里就结束了,把代码放到SmartBI上,测试下就OK了,结果官方文档上说SmartBI报表并不支持Excel本身的宏。我的天!
excel上传模板不支持xlsm格式: https://wiki.smartbi.com.cn/pages/viewpage.action?pageId=76678824

由于不支持excel中的宏的执行,因此excel导入模板不支持xlsm格式的excel文件模板上传,建议将该格式修改成xls或xlsx后进行上传:

也就是说上面的都白忙活了,没法,只能再想办法。
仔细思考了下发现,虽然vba不能用,但是SmartBI人家自己也有宏的,而且是javascript语法的,那写起来不比vba好写多了(对于我来说)。
思路应该也是可以走得通的,就是获取到单元格,然后取他的字号、默认字号、列宽度,再加上原字符串,就可以计算了。但翻了一阵官方文档之后发现前三项都没法得到。那没办法了,前三项实际上只是为了自动计算那个单元格可以容纳的字符数,即然没法自动获取,那就封装成参数,在报表页面调用时手动传一个进去吧。实现的效果大致如下:

SmartBI的服务端宏的代码如下:

function main(spreadsheetReport) {// 将A2单元格里面的长字符串格式化成每19个字符就插入一个硬回车符formatStringToWrapedLine("A19", 92, 0)
}/*** 根据给定的一行可容纳字符数,来插入硬回车* @param cell 要进行格式化的单元格* @param wordsLimit 一行可以容纳的字符数,excel中就是列宽,默认1000个字符* @param sheetIndex 当前工作薄的第几个sheet,默认0,即第一个sheet*/
function formatStringToWrapedLine(cell, wordsLimit, sheetIndex) {// 用于存储for循环中每一行已累计的字符数var count = 0;// 用于拼接结果字符串var resultArr = [];// 第几个sheet,默认0,即第一个sheet页if (!sheetIndex) {sheetIndex = 0}// 每行可容纳字符数限制,默认给1000个if (!wordsLimit) {wordsLimit = 1000;}// 获得sheet页对象var sheet = spreadsheetReport.sheets[sheetIndex]// 获得指定单元格对象var cell = sheet.getCell(cell);// 单元格的值var str = cell.value;// 把原文拆成char数组var arr = str.split('');// 用于判断是否是中文的正则var reg = /[\u4e00-\u9fff]/for (var i = 0; i < arr.length; i++) {// 当前char字符var curStr = arr[i];// 当前字符个数var curLen = 1;// 一个汉字占2个字符,其他的按1个算if (reg.test(curStr)) {curLen = 2;}// 如果当前行已累计的字符数 再加上2个字符长度后 >= 每行的字符限制,则插入Excel里面的硬换行符if (count + 2 >= wordsLimit) {// 插入换行符resultArr.push(String.fromCharCode(10));resultArr.push(String.fromCharCode(13));// 拼接上本轮的字符resultArr.push(curStr);// 重置计数count = 1;} else {// 不需要换行的话就直接拼接即可resultArr.push(curStr);count += curLen;}}// 指定单元格的位置var loc = cell.cellPosition;// 向指定单元格回写格式化后的字符串sheet.setCellValue(loc.row, loc.column, resultArr.join(''));
}

好了,以下就是最初的探索的过程了,如果只是使用的话,就可以不往下看了的。

探索一 单元格像素

首先,参考下面这篇博文
EXCEL的高度和宽度计算-文档值到像素:https://blog.csdn.net/qq_30436011/article/details/126462788

找到了文中引用的官方文档
SheetFormatProperties:https://learn.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.spreadsheet.sheetformatproperties?view=openxml-2.8.1

column:https://learn.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.spreadsheet.column?view=openxml-2.8.1

但是我电脑上的excel打开默认列宽是8.38,和这里给出的例子都不一样,就很痛苦

探索二 字符像素

又找到一篇,可以计算指定字体下,指定字符串的总长度,像素
https://oomake.com/question/1225304
文中提出以下代码来测量字符串像素宽度

'Option Explicit
'API Declares
Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateFontIndirect Lib "gdi32.dll" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function GetTextExtentPoint32 Lib "gdi32.dll" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As SIZE) As Long
Private Declare Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long
Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Const LOGPIXELSY As Long = 90
Private Type LOGFONTlfHeight As LonglfWidth As LonglfEscapement As LonglfOrientation As LonglfWeight As LonglfItalic As BytelfUnderline As BytelfStrikeOut As BytelfCharSet As BytelfOutPrecision As BytelfClipPrecision As BytelfQuality As BytelfPitchAndFamily As BytelfFaceName As String * 32
End Type
Private Type SIZEcx As Longcy As Long
End Type
Public Function getLabelPixel(label As String, fontName As String, fontSize As Integer) As Integer
Dim font As New StdFontDim sz As SIZEfont.Name = fontNamefont.SIZE = fontSize
sz = GetLabelSize(label, font)getLabelPixel = sz.cx
End Function
Private Function GetLabelSize(text As String, font As StdFont) As SIZEDim tempDC As LongDim tempBMP As LongDim f As LongDim lf As LOGFONTDim textSize As SIZE
' Create a device context and a bitmap that can be used to store a' temporary font objecttempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)
' Assign the bitmap to the device contextDeleteObject SelectObject(tempDC, tempBMP)
' Set up the LOGFONT structure and create the fontlf.lfFaceName = font.Name & Chr$(0)lf.lfHeight = -MulDiv(font.SIZE, GetDeviceCaps(GetDC(0), 90), 72) 'LOGPIXELSYlf.lfItalic = font.Italiclf.lfStrikeOut = font.Strikethroughlf.lfUnderline = font.UnderlineIf font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400f = CreateFontIndirect(lf)
' Assign the font to the device contextDeleteObject SelectObject(tempDC, f)
' Measure the text, and return it into the textSize SIZE structureGetTextExtentPoint32 tempDC, text, Len(text), textSize
' Clean up (very important to avoid memory leaks!)DeleteObject fDeleteObject tempBMPDeleteDC tempDC' Return the measurementsGetLabelSize = textSize
End Function

探索三

换个方向,从字符数量上来入手。这个也就是最终结果采用的思路了。
这里先说一下,每个电脑上的Excel都有一个默认字体和大小,以Excel2010为例,这个设置在:文件 -> 选项 -> 常规 ->新建工作簿时这个地方

接下来理思路:

  1. 参考探索一和探索二里面涉及的文章和文档来看,Excel里面的列宽度是指在默认字体及字号下的字符数。就是说如果我们打开一个Excel文件,不手动修改字体和字号的话,那么列宽是几,就可以显示出几个字符(一个汉字占两个字符)。

比如列宽是4时,可以显示4个数字或字母,或是两个中文汉字

再比如列宽是8,则可以显示8个数字或字母,或是4个中文

以上都是偶数,如果是奇数的话,中文可能就会需要换行显示了

这个字符数大致就是这样了。

  1. 来看一下字号,如果我们把字号扩大一倍,那么会怎么样呢,按理说应该是显示字符数会减少一半。以下以我的Excel默认字号是11为基础。
    比如列宽设置4,字号修改为22

    实际测试发现字符宽度正好是4时,我们上面的推论就崩溃了。但是如果我们把宽度稍微拉大一点,比如4.38,就正好了

    比如把列宽拉到8.38时

    这样的话,也就基本符合我们(1)中的推论了。这个多出来的0.38应该就是探索一和探索二的文档中提到的margin padding以及gridline的宽度了吧。
  2. 总结:根据以上两点,我们发现 修改字号后可显示字符数 = 列宽 x 默认字号 / 修改后的字号,比如2中第二个图,可显示的字符数 = 4.38 * 11 / 22 = 2.19 大概就是2个字符。至于这个小数需要怎么取舍,我没有细致研究,有能力的大佬可以把优化后的代码发出来哈。以下是根据这个思路写出来的第一版代码
'判断是否包含中文字符
Function StrWithChinese(StrChk As String) As BooleanStrChk = VBA.StrConv(StrChk, vbNarrow)StrWithChinese = IIf(Len(StrChk) < LenB(StrConv(StrChk, vbFromUnicode)), True, False)
End FunctionSub formatA1()
Dim c As Object
Dim fontName As String, fontSize As Integer, defaultFontSize As Integer
Dim str As String, count As Integer, limit As Integer
Set c = Worksheets("Sheet2").Cells(2, 1)
fontName = c.font.Name
fontSize = c.font.SIZE
Dim resultString As String
'取第一行第1023列这个单元格的字体大小作为默认字体大小
defaultFontSize = Worksheets("Sheet2").Cells(1, 1023).font.SIZE
'MsgBox defaultFontSize
'先获取单元格的宽度,单元格宽度是在字体默认大小下的
limit = WorksheetFunction.Ceiling(c.ColumnWidth * defaultFontSize / fontSize, 1)
str = Worksheets("Sheet2").Cells(1, 1).Value
For i = 1 To Len(str)Dim cur As Integer, curStr As String'当前字符curStr = Mid(str, i, 1)'中文算2个,其他算1个If StrWithChinese(curStr) Thencur = 2Elsecur = 1End If'这里直接写了个2是因为有时候中英文一起的计算当前行已有宽度时会有个正好相等的问题If (count + 2) >= limit Then'如果再加上当前字符后,当前行长度就等于单元格宽度了,则插入一个换行符resultString = resultString & Chr(10) & Chr(13) & curStr' count从头开始统计count = 1'MsgBox ("restart count:" & count & " limit:" & limit & " cur:" & cur & " curStr:" & Mid(str, i, 1) & " result:" & resultString)Else'统计当前行字符数count = count + cur'拼接结果字符resultString = resultString & curStr'MsgBox ("continue count:" & count & " limit:" & limit & " cur:" & cur & " curStr:" & Mid(str, i, 1) & " result:" & resultString)End If
Next
c.Value = resultString
End Sub

关于Excel自动换行,不会在西文单词中间换行的问题相关推荐

  1. PPT/Word中英文单词换行问题 (取消了西文在单词中间换行的选项,但英文部分依然不连续) 的解决方法

    文章目录 一.问题描述 二.失败的尝试 三.解决方法 方法一 方法二 写在最后 一.问题描述 笔者在制作英语PPT的时候 ,由于贪图方便 ,直接将中文翻译成英文,从有道翻译直接Crtl+V粘贴了,出现 ...

  2. word中允许西文在单词中间换行

    word中允许西文在单词中间换行 参考网址:https://www.kafan.cn/A/y31o4pp8vk.html 在word中有时候需要输入英文单词,但发现输入的单词有些连不到一起,英文单词在 ...

  3. excel 自动换行后批量设置单元格上下边距

    excel 自动换行后 单元格的上下边距挨的很紧,看起来很不舒服 如下图 现在教大家 如何批量设置 单元格中有多行文本后的上下间距,设置成功后 如下图所示: 具体步骤: 1.鼠标悬停在 excel 中 ...

  4. html的excel表格自动换行,excel自动换行按哪个键:在Excel中实现自动换行简单实用...

    excel自动换行按哪个键:在Excel中实现自动换行简单实用 Excel单元格都有默认的宽度,如果在其中输入的文本过长,会超出单元格的范围,但此时Excel并不会给它自动换行,这还需要我们来设置.小 ...

  5. Word 允许西文在单词中间换行,没用/无效 终极办法

    有时在写论文中,英文的调整相当麻烦,为了节约版面,会设置允许西文在单词中间换行.但有时不希望这样,特别是在复制网上英文时,会出现单词分断换行情况,如何解决: 1.一般办法. 在Word选择要调整的英文 ...

  6. Excel怎么换行?简单!Excel大神教会了我N种换行方法

    平时工作中大家几乎都会跟Excel有所接触,各种统计表.人员名单等等都离不开Excel来制作,但是入手容易上手难的Excel还是让很多人迷茫的无从下手,例如在Excel单元格中的换行问题,直接按Ent ...

  7. css 长单词不换行溢出容器的解决方法 word-wrap与word-break

    这两个东西是什么,我相信至今还有很多人搞不清,只会死记硬背的写一个word-wrap:break-word;word-break:break-all;这样的东西来强制断句,又或者是因为这两个东西实在是 ...

  8. Java操作excel之合并单元格、写入强制换行数据

    1.初始化Excel表格 HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet(&quo ...

  9. display:table-cell自适应布局下连续单词字符换行

    1. display:table-cell自适应布局 典型的双栏布局类名使用如下: 这种方式实现的自适应布局,元素宽度无需定值,且margin(浮动部分)与padding自由设置,支持百分比宽度(ta ...

最新文章

  1. 软件架构设计学习总结(1):标准Web系统的架构分层
  2. Web自适应布局你需要知道的所有事儿
  3. java虚拟机监控_Java虚拟机监控工具
  4. 这一套磁悬浮PID训练装置,你不能错过。
  5. 多种方法解决Exchange 2010 EMC批量启用邮箱之后出..
  6. 谷歌在外贸中的实战解析
  7. Linux ANSYS FLUENT 在集群上的安装与配置
  8. c语言答辩ppt案例,c语言ppt例子课题答辩ppt成品中南民族大学.ppt
  9. php繁体类,PHP类UTF8编码内的繁简转换-繁体-简体
  10. 服务器装usb无线网卡,小巧便携的USB无线网卡,让台式电脑实现wifi自由
  11. megafiers3.14下载
  12. K3S高可用安装体验
  13. Paper:《Pre-Trained Models: Past, Present and Future大规模预训练模型的发展历史、最新现状和未来发展三个方向》翻译与解读
  14. 毫末追击智能驾驶1000天,对战蔚小理将现胜负手
  15. opencv区域提取
  16. 计算机基础知识之工作总结,计算机教师工作总结(精选3篇)
  17. jconsole不能连接本地idea程序
  18. C语言”%p”的意思-----printf(%p,p)
  19. 一个Flash开发者从入门到放弃的成长之路
  20. SetFocus 方法

热门文章

  1. 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(八)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(一)
  2. JavaScript——计算平方值
  3. Integer.valueOf、intValue、Integer.parseInt使用
  4. AlexNet网络的搭建
  5. LAMP - 学习/实践
  6. DBA日-周-月工作职责
  7. 2022-01-18 任何的长相厮守、不离不弃都是术业有专攻的基础。遇见3D MAX只叹恨晚。。。
  8. 数据处理(一)数据清洗
  9. pycharm中导入pyQt无法使用
  10. Echarts不显示X轴Y轴网格线