幽兰聆雨 发表于 2025-5-23 17:20:39

求教,C#写插件时遇到的乱码问题。

从DBText.TextString属性拿文字,然后把文字和其他文字拼接起来组成html格式的String文本放入剪切板。把这个文本在CAD打印出来没问题,但是存入剪切板后中文变成了乱码,数字、英文没问题,怎么解决?

幽兰聆雨 发表于 2025-5-23 17:44:11

本帖最后由 幽兰聆雨 于 2025-5-23 17:48 编辑

你有种再说一遍 发表于 2025-5-23 17:29
看看你的剪贴板代码,直接存二进制就好了


构建html表格、写入剪切板
public string ToHtml()
{
    StringBuilder sb = new StringBuilder();
    sb.Append("<table border='1'>");
    for (int row = 0; row < RowCount; row++)
    {
      sb.Append("\n<tr>");
      for (int col = 0; col < ColumnCount; col++)
      {
            if (_cells.TryGetValue((row, col), out var cell))
            {
                if (cell.MergeRange.HasValue)
                {
                  // 处理合并单元格                           
                  var (mergeRows, mergeCols) = cell.MergeRange.Value;
                  string colspan = mergeCols > 0 ? $" colspan='{mergeCols + 1}'" : "";
                  string rowspan = mergeRows > 0 ? $" rowspan='{mergeRows + 1}'" : "";
                  sb.Append($"<td{colspan}{rowspan}>{cell.Value?.ToString() ?? ""}</td>");
                }
                else if (!cell.IsMerged)
                {
                  sb.Append($"<td>{cell.Value?.ToString() ?? ""}</td>");
                }
            }
            else
            {
                sb.AppendLine("<td></td>");
            }
      }
      sb.Append("</tr>");
    }
    sb.Append("\n</table>");
    return sb.ToString();
}
public string ToCsv()
{
    StringBuilder sb = new StringBuilder();
    for (int row = 0; row < RowCount; row++)
    {
      for (int col = 0; col < ColumnCount; col++)
      {
            if (_cells.TryGetValue((row, col), out var cell))
            {
                sb.Append(cell.Value?.ToString() ?? "");
            }
            else
            {
                sb.Append("");
            }
            if (col < ColumnCount - 1)
                sb.Append(",");
      }
      sb.AppendLine();
    }
    return sb.ToString();
}

public void WriteToClipboard()
{
    // 准备数据
    string html = ToHtml();
    // string csv = ToCsv();
    string text = ToString();
    // 创建 DataObject 并添加多格式数据
    DataObject data = new DataObject();

    // 添加HTML
    data.SetData(DataFormats.Html, HtmlOperator.GetFullHtml(html));

    // 添加CSV
    // data.SetData(DataFormats.CommaSeparatedValue, csv);

    // 添加纯文本
    data.SetData(DataFormats.Text, text);

    // 一次性写入剪贴板
    Clipboard.SetDataObject(data, true); // true表示程序退出后数据仍保留
}






// 将完善html

public static string GetFullHtml(string htmlContent)
{
    // 包装 HTML 内容为剪切板兼容格式
    string header = $"Version:0.9\nStartHTML:0000000000\nEndHTML:0000000000\nStartFragment:0000000000\nEndFragment:0000000000\n<!DOCTYPE html>\n<html>\n<head>\n<meta charset='utf-8'>\n</head>\n<body>\n<!--StartFragment-->\n";
    string none = "\n<!--读史使人明智,读诗使人灵秀,数学使人周密,科学使人深刻,伦理学使人庄重,逻辑修辞之学使人善辩:凡有所学,皆成性格。-->\n<!--md老是出错,感觉是结束位置计算有问题,加点废话[成功了,果然是结束位置的问题]。-->\n<!--by YLLY-->";
    string footer = $"\n<!--EndFragment-->\n</body>\n</html>";
    string clipboardData =header+ htmlContent + none +footer;

    // 计算各个部分的偏移量
    return UpdateHtmlClipboardOffsets(clipboardData);
}
// 更新 HTML 剪切板格式中的偏移量
private static string UpdateHtmlClipboardOffsets(string fullHtml)
{
    // 计算字节偏移量(UTF-8编码)
    byte[] bytes = Encoding.UTF8.GetBytes(fullHtml);
    int startHtml = Encoding.UTF8.GetString(bytes).IndexOf("<!DOCTYPE html>");
    int startFragment = Encoding.UTF8.GetString(bytes).IndexOf("<!--StartFragment-->") + "<!--StartFragment-->".Length;
    int endFragment = Encoding.UTF8.GetString(bytes).IndexOf("<!--EndFragment-->");
    int endHtml = bytes.Length;

    // 替换占位符
    fullHtml = fullHtml
      .Replace("StartHTML:0000000000", $"StartHTML:{startHtml:D10}")
      .Replace("EndHTML:0000000000", $"EndHTML:{endHtml:D10}")
      .Replace("StartFragment:0000000000", $"StartFragment:{startFragment:D10}")
      .Replace("EndFragment:0000000000", $"EndFragment:{endFragment:D10}");
    return fullHtml;
}

幽兰聆雨 发表于 2025-5-23 18:12:59

你有种再说一遍 发表于 2025-5-23 17:50
别用 Clipboard.SetDataObject
去用 Win32API

ai写了一个,还是乱码。。。

粘到WPS里面都没啥问题,粘到excel里面就乱码,麻了
// Win32 API常量
private const uint CF_TEXT = 1;
private static readonly uint CF_HTML = RegisterClipboardFormat("HTML Format");

// Win32 API函数

private static extern bool OpenClipboard(IntPtr hWndNewOwner);


private static extern bool CloseClipboard();


private static extern bool EmptyClipboard();


private static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);


private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);


private static extern IntPtr GlobalLock(IntPtr hMem);


private static extern bool GlobalUnlock(IntPtr hMem);


private static extern uint RegisterClipboardFormat(string lpszFormat);
public static void WriteToClipboard(string html,string text)
{
   // 打开剪贴板
   if (OpenClipboard(IntPtr.Zero))
   {
         try
         {
             // 清空剪贴板
             EmptyClipboard();

             // 写入HTML格式
             if (!string.IsNullOrEmpty(html))
             {
               SetClipboardText(html, CF_HTML);
             }

             // 写入纯文本格式
             if (!string.IsNullOrEmpty(text))
             {
               SetClipboardText(text, CF_TEXT);
             }
         }
         finally
         {
             // 确保关闭剪贴板
             CloseClipboard();
         }
   }
}

private static void SetClipboardText(string text, uint format)
{
   // 分配全局内存
   IntPtr hGlobal = GlobalAlloc(0x2000, (UIntPtr)(text.Length + 1));
   if (hGlobal != IntPtr.Zero)
   {
         try
         {
             // 锁定内存并复制数据
             IntPtr pGlobal = GlobalLock(hGlobal);
             if (pGlobal != IntPtr.Zero)
             {
               byte[] bytes = Encoding.UTF8.GetBytes(text);
               Marshal.Copy(bytes, 0, pGlobal, bytes.Length);
               Marshal.WriteByte(pGlobal + bytes.Length, 0); // 添加null终止符
               GlobalUnlock(pGlobal);
               // 设置剪贴板数据
               SetClipboardData(format, hGlobal);
             }
         }
         catch
         {
             // 发生错误时释放内存
             Marshal.FreeHGlobal(hGlobal);
             throw;
         }
   }
}

你有种再说一遍 发表于 2025-5-23 19:03:56

幽兰聆雨 发表于 2025-5-23 18:56
乱码的地方就是表格内容中的中文部分,中文编码区别怎么对比啊?我刚才试了一下把得到的html文本存到文本 ...

官网:
https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format?app_lang=zh-CN

估计是:
在C#代码中将HTML粘贴到Excel出现中文乱码的问题,通常与编码或Excel的解析方式有关。以下是可能的原因和解决方案:

1. HTML未明确指定UTF-8编码

问题:如果生成的HTML没有在<meta>标签中声明UTF-8编码,Excel可能无法正确解析中文字符。

解决方案:确保HTML头部包含:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

C#代码示例:

string html = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8""></head><body>中文内容</body></html>";

2. Excel的编码解析问题

问题:即使HTML是UTF-8,Excel在粘贴时可能默认使用其他编码(如ANSI)。

解决方案:

方法1:将HTML保存为.html文件后用Excel直接打开(Excel会识别编码)。

方法2:通过剪贴板API显式设置编码(复杂,需调用Windows API)。

3. 剪贴板数据格式问题

问题:直接复制HTML文本到剪贴板时,可能丢失编码信息。

解决方案:使用Clipboard.SetText并指定格式为TextDataFormat.Html(需处理HTML头):

Clipboard.SetText(html, TextDataFormat.Html);

注意:HTML剪贴板格式需要包含特定的头信息(参考微软文档)。

4. 文件保存时的编码问题

问题:如果代码将HTML保存到文件,需确保文件以UTF-8编码写入。

解决方案:

File.WriteAllText("output.html", html, Encoding.UTF8);

5. Excel的区域设置影响

问题:Excel可能根据系统区域设置自动选择编码。

临时解决:在Excel中手动选择“数据” → “从文本/CSV”导入,并指定UTF-8编码。

推荐调试步骤

检查生成的HTML文件:用记事本/浏览器打开,确认中文正常显示。

直接通过文件导入Excel:测试是否仍乱码。

简化HTML内容:排除CSS/特殊标签的干扰。

如果仍有问题,可以提供部分代码片段,我可以进一步分析具体实现逻辑。

你有种再说一遍 发表于 2025-5-23 17:29:28

看看你的剪贴板代码,直接存二进制就好了

你有种再说一遍 发表于 2025-5-23 17:50:54

别用 Clipboard.SetDataObject
去用 Win32API

幽兰聆雨 发表于 2025-5-23 17:51:52

你有种再说一遍 发表于 2025-5-23 17:29
看看你的剪贴板代码,直接存二进制就好了

想做一个利用剪切板存放html表格,实现将CAD表格输出到excel、word、txt文件的功能,直接存也可以吗?

幽兰聆雨 发表于 2025-5-23 17:55:48

你有种再说一遍 发表于 2025-5-23 17:50
别用 Clipboard.SetDataObject
去用 Win32API

感谢大佬,我去学一下

幽兰聆雨 发表于 2025-5-23 18:18:31

幽兰聆雨 发表于 2025-5-23 18:12
ai写了一个,还是乱码。。。

粘到WPS里面都没啥问题,粘到excel里面就乱码,麻了

哦不对,这是之前的,调试没更新成功

你有种再说一遍 发表于 2025-5-23 18:19:58

幽兰聆雨 发表于 2025-5-23 18:18
哦不对,这是之前的,调试没更新成功

你反向操作,从excel复制,然后比较两者数据差异

幽兰聆雨 发表于 2025-5-23 18:23:20

你有种再说一遍 发表于 2025-5-23 18:19
你反向操作,从excel复制,然后比较两者数据差异

从excel复制已经试过了,没问题
页: [1] 2
查看完整版本: 求教,C#写插件时遇到的乱码问题。