python - jinja + 表单 + unicode 控制字符 + xml/docx 集成
问题描述
我正在根据用户在表单中输入的内容创建 Word 文档。但是,当用户输入一个 unicode 控制字符,并尝试使用 python-docx 包从中创建一个 word 文件时,会发生此错误:
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
则执行该功能。如果未选中该复选框,则不会执行该复选框,并且将显示提到的服务器错误。
控制字符为:U+000C : <control-000C> (FORM FEED [FF])
解决方案
unicode 中有大量的控制字符。因此,基本上,您需要删除控制字符,这是 unicode 字符中的类别之一。为此,我建议您使用unicodedata模块中的unicodedata.category 。
请参见下面的代码:
import unicodedata
def remove_control_chars(s):
return "".join(ch for ch in s if unicodedata.category(ch)[0] != "C")
推荐阅读
- java - J8 流按列表中的向量排序
> - python - 将嵌套的 JSON 展平为 pandas.DataFrame:基于字典值的排序和命名列
- python - Python 分类模型
- three.js - Three.JS - 补间相机位置
- android - RecyclerView - notifyDataSetChanged() - Kotlin - 不工作
- spring-cloud-stream - 将 Spring Cloud Sleuth 与 OpenZipkin 一起使用。GUI 未显示所有跨度
- audio - 处理音频点击/弹出声音的算法
- css - 我正在尝试对具有最大和最小宽度的背景颜色应用媒体查询,但浏览器在不同维度上应用查询
- svg - SVG 过滤器 - feColorMatrix 具有非线性颜色
- javascript - mongodb查询聚合,检查一个键对应一个值的次数