jinja +形式+ unicode控制字符+ xml / docx集成

时间:2018-08-19 20:00:15

标签: python flask jinja2 lxml python-docx

我正在根据用户在表格中输入的内容来创建Word文档。但是,当用户输入unicode控制字符,并尝试使用python-docx软件包从其中生成一个单词文件时,会发生以下错误:

File "src\lxml\apihelpers.pxi", line 1439, in lxml.etree._utf8
ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters

我设法解决了这个问题,方法是在每次请求之前检查表单中是否有无效的xml字符(我有很多可能发生此问题的表单),并从字段中删除了所有无效的xml字符。然后,我制作一个新的不可变多字典,并用清除后的文本填充它。

from docx import Document
from docx.shared import Inches
from flask import Flask, render_template_string, request
from werkzeug.datastructures import ImmutableMultiDict

def valid_xml_char_ordinal(c):
    codepoint = ord(c)
    return (0x20 <= codepoint <= 0xD7FF or codepoint in (0x9, 0xA, 0xD) or
            0xE000 <= codepoint <= 0xFFFD or 0x10000 <= codepoint <= 0x10FFFF)

app = Flask(__name__)

@app.before_request
def before_request():
    if 'check_form_xml_validity' in request.form:
        tuple_list = []
        for field_name in request.form:
            all_field_values = request.form.getlist(field_name)
            for field_value in all_field_values:
                cleaned_field_value = ''.join(c for c in field_value if valid_xml_char_ordinal(c))
                tuple_list.append((field_name, cleaned_field_value))
        request.form = ImmutableMultiDict(tuple_list)

@app.route('/', methods=['GET', 'POST'])
def form_test():
    if request.method == 'GET':
        x = '' # this seemingly empty string is not empty, but contains a bunch of control characters
        return render_template_string(
            """<form action="{{ url_for('form_test') }}" method="post">
                <input name="some_field" value="{{x}}"><br>
                check the xml validity of this form? <br>
                <input type="checkbox" checked name="check_form_xml_validity"><br>
                <button>submit</button>
            </form>""",
            x=x)
    else:
        doc = Document()
        p = doc.add_paragraph(request.form['some_field'])
        return 'yay'

并且此方法完美地工作。但是,我似乎不可能是唯一一个遇到此问题的人,但是我找不到任何干净的解决方案。所以问题是,我真的应该以当前的方式解决这个问题吗?这很繁琐,感觉就像我在某个地方可以解决此问题的Flask或python-docx设置或参数一样。

该示例具有完整的功能,并且如果选中了此复选框,则将执行before_request函数。如果未选中该复选框,则不会执行该复选框,并且将显示上述服务器错误。

enter image description here

控制字符为:U+000C : <control-000C> (FORM FEED [FF])

1 个答案:

答案 0 :(得分:2)

unicode中有大量的控制字符。因此,基本上,您需要删除控制字符,这是Unicode字符中的类别之一。为此,我建议您使用unicodedata.category模块中的unicodedata

请参见下面的代码:

import unicodedata


def remove_control_chars(s):
    return "".join(ch for ch in s if unicodedata.category(ch)[0] != "C")