【问题标题】:NodeJS RTF ANSI Find and Replace Words With Special CharsNodeJS RTF ANSI 用特殊字符查找和替换单词
【发布时间】:2019-11-23 16:10:45
【问题描述】:

我有一个查找和替换脚本,当单词没有任何特殊字符时,它可以正常工作。但是,很多时候会出现特殊字符,因为它正在查找名称。到目前为止,这正在破坏脚本。

脚本查找{<some-text>} 并尝试替换内容(以及删除大括号)。

例子:

text.rtf

Here's a name with special char {Kotouč}

script.ts

import * as fs from "fs";

// Ingest the rtf file.
const content: string = fs.readFileSync("./text.rtf", "utf8");
console.log("content::\n", content);

// The string we are looking to match in file text.
const plainText: string = "{Kotouč}";

// Look for all text that matches the patter `{TEXT_HERE}`.
const anyMatchPattern: RegExp = /{(.*?)}/gi;
const matches: string[] = content.match(anyMatchPattern) || [];
const matchesLen: number = matches.length;
for (let i: number = 0; i < matchesLen; i++) {

    // It correctly identifies the targeted text.
    const currMatch: string = matches[i];
    const isRtfMetadata: boolean = currMatch.endsWith(";}");
    if (isRtfMetadata) {
        continue;
    }

    // Here I need a way to escape `plainText` string so that it matches the source.
    console.log("currMatch::", currMatch);
    console.log("currMatch === plainText::", currMatch === plainText);
    if (currMatch === plainText) {
        const newContent: string = content.replace(currMatch, "IT_WORKS!");
        console.log("newContent:", newContent);
    }
}

输出

content::
 {\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0

\f0\fs24 \cf0 Here's a name with special char \{Kotou\uc0\u269 \}.}

currMatch:: {Kotou\uc0\u269 \}

currMatch === plainText:: false

它看起来像 ANSI 转义,我尝试使用 jsesc,但它产生了一个不同的字符串,{Kotou\u010D},而不是文档产生的 {Kotou\uc0\u269 \}

如何动态转义 plainText 字符串变量,使其与文档中的内容匹配?

【问题讨论】:

标签: node.js escaping rtf ansi ansi-escape


【解决方案1】:

我需要加深对 rtf 格式以及一般文本编码的了解。

从文件中读取的原始 RTF 文本给了我们一些提示:

{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600...

这部分 rtf 文件元数据告诉我们一些事情。

它使用的是RTF文件格式版本1。编码是ANSI,具体是cpg1252,也称为Windows-1252CP-1252,即:

...拉丁字母的单字节字符编码

(source)

其中有价值的信息是我们知道它使用的是拉丁字母,这将在以后使用。

知道使用的特定 RTF 版本我偶然发现了RTF 1.5 Spec

快速搜索我正在研究的其中一个转义序列的规范,发现它是一个 RTF 特定的转义控制序列,即\uc0。所以知道我能够解析我真正想要的东西,\u269。现在我知道它是 unicode 并且有一个很好的预感\u269 代表unicode character code 269。所以我查了一下...

\u269(字符代码269shows up on this page to confirm。现在我知道了字符集以及获得等效纯文本(未转义)需要做什么,并且有一个基本的SO post I used here 来启动该功能。

利用所有这些知识,我能够从那里拼凑起来。这是完整的更正脚本及其输出:

script.ts

import * as fs from "fs";


// Match RTF unicode control sequence: http://www.biblioscape.com/rtf15_spec.htm
const unicodeControlReg: RegExp = /\\uc0\\u/g;

// Extracts the unicode character from an escape sequence with handling for rtf.
const matchEscapedChars: RegExp = /\\uc0\\u(\d{2,6})|\\u(\d{2,6})/g;

/**
 * Util function to strip junk characters from string for comparison.
 * @param {string} str
 * @returns {string}
 */
const cleanupRtfStr = (str: string): string => {
    return str
        .replace(/\s/g, "")
        .replace(/\\/g, "");
};

/**
 * Detects escaped unicode and looks up the character by that code.
 * @param {string} str
 * @returns {string}
 */
const unescapeString = (str: string): string => {
    const unescaped = str.replace(matchEscapedChars, (cc: string) => {
        const stripped: string = cc.replace(unicodeControlReg, "");
        const charCode: number = Number(stripped);

        // See unicode character codes here:
        //  https://unicodelookup.com/#latin/11
        return String.fromCharCode(charCode);
    });

    // Remove all whitespace.
    return unescaped;
};

// Ingest the rtf file.
const content: string = fs.readFileSync("./src/TEST.rtf", "binary");
console.log("content::\n", content);

// The string we are looking to match in file text.
const plainText: string = "{Kotouč}";

// Look for all text that matches the pattern `{TEXT_HERE}`.
const anyMatchPattern: RegExp = /{(.*?)}/gi;
const matches: string[] = content.match(anyMatchPattern) || [];
const matchesLen: number = matches.length;
for (let i: number = 0; i < matchesLen; i++) {
    const currMatch: string = matches[i];
    const isRtfMetadata: boolean = currMatch.endsWith(";}");
    if (isRtfMetadata) {
        continue;
    }

    if (currMatch === plainText) {
        const newContent: string = content.replace(currMatch, "IT_WORKS!");
        console.log("\n\nnewContent:", newContent);
        break;
    }

    const unescapedMatch: string = unescapeString(currMatch);
    const cleanedMatch: string = cleanupRtfStr(unescapedMatch);
    if (cleanedMatch === plainText) {
        const newContent: string = content.replace(currMatch, "IT_WORKS_UNESCAPED!");
        console.log("\n\nnewContent:", newContent);
        break;
    }
}

输出

content::
 {\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0

\f0\fs24 \cf0 Here\'92s a name with special char \{Kotou\uc0\u269 \}}


newContent: {\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0

\f0\fs24 \cf0 Here\'92s a name with special char \IT_WORKS_UNESCAPED!}

希望对不熟悉字符编码/转义以及它用于 rtf 格式文档的其他人有所帮助!

【讨论】:

    猜你喜欢
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-18
    • 1970-01-01
    • 2015-03-03
    • 1970-01-01
    相关资源
    最近更新 更多