【问题标题】:WordprocessingDocument separates text (unnecessarily)WordprocessingDocument 分隔文本(不必要)
【发布时间】:2013-06-24 12:21:38
【问题描述】:

我正在尝试替换作为模板的 word 文档中的某些位置。这些地方被标记为以@ 开头的单词。例如@Name。我选择所有文本,用我的函数遍历它们并替换。这背后的所有逻辑都运行良好。但是,由于我不知道的原因,文本的定义简单如下:

List<Text> texts = w.MainDocumentPart.Document.Descendants<Text>().ToList();

不是我期望的那样。例如(文本的简短列表,每行一个文本):

Document date: 
@
date
, written by: 
@Name 
@
Surname

即使它应该像这样在一行中:Document date: @date, written by: @Name @Surname。如您所见,在@date@Surname 等情况下,我无法正确使用我的替换方法,因为没有@date@Surname 积​​极到位。所以我改进了我的代码,但现在我发现其中有很多缺陷,所以它将遗漏的@'s 连接到下一个文本。不幸的是,我在一个表格中找到了一个文本,它完全杀死了我所有的算法。 是这样的:

(@
sum_
words)

即使我没有专门用不同的样式格式化它。如您所见,它必须是(@sum_words),在这种情况下,我的算法很容易被替换。不幸的是,我做不到。因此,我的问题是:

  1. 为什么即使我没有制作零件,它也会分成几部分 这个词有什么不同吗?
  2. 有没有办法解决这个问题,所以我的 文字可以完好无损吗?

更新

到目前为止我能做的最好的事情是:

for (int i = texts.Count - 1; i > 0; i--)
{
    if (texts[i - 1].Text.EndsWith("@") || texts[i - 1].Text.EndsWith("_"))
    {
        texts[i - 1].Text = texts[i - 1].Text + texts[i].Text;
        texts[i].Text = "";
    }
}

如果它们被中途分割,它会连接文本(据我所知,它们只会在@_ 等特定于我需要的字符上分割。循环是相反的,因为它会产生更多保存组中第一个文本部分的属性而不是最后一个(例如在元素之前保留间距)是有意义的。

更新 2

这是请求的表格单元格:

<w:tc>
    <w:tcPr>
        <w:tcW w:w="2410" w:type="dxa"/>
        <w:tcBorders>
            <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
        </w:tcBorders>
        <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
    </w:tcPr>
    <w:p w:rsidR="009D473B" w:rsidRDefault="00385754" w:rsidP="00385754">
        <w:pPr>
            <w:jc w:val="center"/>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
        </w:pPr>
        <w:r>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>@sum</w:t>
        </w:r>
        <w:r w:rsidR="009D473B">
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t xml:space="preserve">(@</w:t>
        </w:r>
        <w:r>
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>sum_</w:t>
        </w:r>
        <w:r w:rsidR="009D473B">
            <w:rPr>
                <w:lang w:val="lt-LT"/>
            </w:rPr>
            <w:t>words)</w:t>
        </w:r>
    </w:p>
</w:tc>

【问题讨论】:

    标签: c# xml formatting wordprocessingml word-processor


    【解决方案1】:

    我在 WordML 文档中替换变量时遇到的唯一问题是由拼写和语法模块引起的,这些模块往往会插入特殊标签,这会破坏您的变量名称,这是通过在模板期间停用 Word 中语法错误的完全检查和标记来解决的编辑。

    我的替换方法是这样的(我命名变量的约定有点不同,这个是你的):

    public static void Assign(XElement xe, string name, string value)
    {
        XElement el = xe.Descendants()
            .FirstOrDefault(e => e.Name.LocalName == "t" && e.Value.Contains("(@" + name + ")"));
        if (el != null)
        {
            el.Value = el.Value.Replace("(@" + name + ")", value);
        }
        else
        {
            AssignFallback(xe, name, value);
        }   
    }
    

    编辑 2

    我写了一个替换变量的备用方法,以防上面的方法不起作用(case el == null)。一个段落 w:p 可以包含多个运行,并且我们的变量名可以在一系列连续的运行中被打破。所以我们想识别那些并只在第一个替换值,然后删除其余的。我们必须小心保留可能出现在变量名之前和之后的文本(前缀将在第一次运行中,后缀在最后一次运行中)。

    public static void AssignFallback(XElement xe, string name, string value)
    {
        string varName = "(@" + name + ")";
        XElement xep = xe.Descendants()
                .FirstOrDefault(x => x.Name.LocalName == "p" && x.Value.Contains(varName));
        if (xep == null)
        {
            return;
        }
        string prefix = "", sufix = "";
        List<XElement> truns = new List<XElement>();
        List<XElement> allruns = xep.Descendants().Where(x => x.Name.LocalName == "r").ToList();
        for (int i = 0; i < allruns.Count; i++)
        {
            if (!allruns[i].Value.Contains("(@"))
            {
                continue;
            }
            int index = allruns[i].Value.IndexOf("(@");
            prefix = allruns[i].Value.Substring(0, index);
            truns.Clear();
            truns.Add(allruns[i]);
            string nameTemp = allruns[i].Value.Substring(index, allruns[i].Value.Length - index);
            if (!varName.StartsWith(nameTemp))
            {
                continue;
            }
            for (int j = i + 1; j < allruns.Count; j++)
            {
                nameTemp += allruns[j].Value;
                truns.Add(allruns[j]);
                if (nameTemp.StartsWith(varName))
                {
                    sufix = nameTemp.Substring(varName.Length);
                    break;
                }
                else if (nameTemp.Length > varName.Length)
                {
                    break;
                }
            }
            if (nameTemp.StartsWith(varName))
            {
                XElement xet = truns[0].Descendants().FirstOrDefault(x => x.Name.LocalName == "t");
                xet.Value = prefix + value + sufix;
                for (int j = 1; j < truns.Count; j++)
                {
                    truns[j].Remove();
                }
            }
        }
    }
    

    【讨论】:

    • 因为检查和标记是在创建模板本身时完成的,一旦我在 C# 部分打开文档本身,是否可以手动删除这些标签、属性、属性等?例如一些MagicallyRemoveCheckingMarking(w)。因为我确实看到说服某人不使用这些是多么不可能,更不用说,要使其发挥作用,每个人都必须这样做。但是,如果只有一种方法可以在之后删除这些插入...?
    • 一旦您有一个 XDocument 或 XElement 对您的文档部分的引用,一切皆有可能,唯一的限制是修改后成为有效的 Word 文档。如果您发布有问题的部分的确切 xml 结构,我可以帮助您。它应该看起来像 ...(@variable_name)... 如果它是 Word 2007+ 文档,您可以提取 .docx 文件使用您拥有的任何 zip 程序(例如 7-zip)到一个文件夹,或更改 ext。到 .zip 并正常打开。然后寻找 \word\document.xml 文件。对于保存为 Word XML / Word 2003 XML 的 Word 文档,您将拥有一个 xml 文件。
    • 我按要求更新了问题。感谢您查看:)
    • 我必须通知,我的变量看起来像 @name 而不是 (@name),也许其中一个例子让你这么想。这会影响你的方法吗?
    • 您可以将方法调整为您命名变量的约定。在 AssignFallback 中,请参见 varName,并且对于 ...Value.Contains("(@"))... 和 ...Value.Contains("(@")),将 "(@" 替换为 "@"。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 2010-10-03
    • 2010-09-22
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多