有没有办法将文本从Unicode缩减为ASCII?

时间:2011-04-13 14:45:49

标签: mapping ascii special-characters varchar unicode-normalization

对于每个ASCII字符,我需要的是类似的Unicode字符列表。

问题是当人们输入文档时,像Microsoft Excel和Word这样的程序会插入非ASCII双引号,单引号,破折号等。我想将此文本存储在“varchar”类型的数据库字段中,该字段需要单字节字符。

为了存储ASCII(单字节)文本,可以认为其中一些Unicode字符与特定的ASCII字符相当或相似,用等效的ASCII字符替换Unicode字符就可以了。

我想要一个像MapToASCII这样的简单函数,它可以将Unicode文本转换为ASCII等价物,允许我为任何与任何ASCII字符都不相似的Unicode字符指定替换字符。

2 个答案:

答案 0 :(得分:1)

Win32 API WideCharToMultiByte可用于此转换(Unicode到ANSI)。使用CP_ACP作为第一个参数。这样的事情可能比尝试构建自己的映射函数更好。

编辑冒着听起来像我正试图将其作为解决OP愿望的解决方案的风险,似乎值得指出这个API做了很多(全部?)的什么是要求。目标是尽可能地将(尽管)Unicode字符串映射到“ANSI”(在这种情况下ANSI可能是移动目标的东西)。另一个要求是能够为那些无法映射的字符指定一些替代字符。以下示例执行此操作。它将Unicode字符串“转换”为char,并对那些无法转换的字符使用下划线(倒数第二个参数)。

ret = WideCharToMultiByte( CP_ACP, 0, L"abc個חあЖdef", -1, 
                           ac, sizeof( ac ), "_", NULL );
for ( i = 0; i < strlen( ac ); i++ )
  printf( "%c %02x\n", ac[i], ac[i] );

答案 1 :(得分:0)

这里有一个高度相关的问题:Replacing unicode punctuation with ASCII approximations

虽然答案不够,但它给了我一个想法。我可以将Basic Multilingual Plane(0)中的每个Unicode代码点映射到等效的ASCII字符(如果存在)。以下C#代码将通过创建HTML表单来帮助您在其中为每个值键入替换字符。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.IO;

namespace UnicodeCharacterCategorizer
{
    class Program
    {
        static void Main(string[] args)
        {
            string output_filename = "output.htm"; //set a filename if not specifying one through the command line
            Dictionary<UnicodeCategory,List<char>> category_character_sets = new Dictionary<UnicodeCategory,List<char>>();
            foreach (UnicodeCategory c in Enum.GetValues(typeof(UnicodeCategory)))
                category_character_sets.Add( c, new List<char>() );
            for (int i = 0; i <= 0xFFFF; i++)
            {
                if (i >= 0xD800 && i <= 0xDFFF) continue; //Skip ranges reserved for high/low surrogate pairs.
                char c = (char)i;
                UnicodeCategory category = char.GetUnicodeCategory( c );
                category_character_sets[category].Add( c );
            }
            StringBuilder file_data = new StringBuilder( @"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd""><html xmlns=""http://www.w3.org/1999/xhtml""><head><title>Unicode Category Character Sets</title><style>.categoryblock{border:3px solid black;margin-bottom:10px;padding:5px;} .characterblock{display:inline-block;border:1px solid grey;padding:5px;margin-right:5px;} .character{display:inline-block;font-weight:bold;background-color:#ffeeee} .numericvalue{color:blue;}</style></head><body><form id=""charactermap"">" );
            foreach (KeyValuePair<UnicodeCategory,List<char>> entry in category_character_sets)
            {
                file_data.Append( @"<div class=""categoryblock""><h1>" + entry.Key.ToString() + ":</h1><br />" );
                foreach (char c in entry.Value)
                {
                    string hex_value = ((int)c).ToString( "x" );
                    file_data.Append( @"<div class=""characterblock""><span class=""character"">&#x" + hex_value + @";<br /><span class=""numericvalue"">" + hex_value + @"</span><br /><input type=""text"" name=""r_" + hex_value + @""" /></div>" );
                }
                file_data.Append( "</div>" );
            }
            file_data.Append("</form></body></html>" );
            File.WriteAllText( output_filename, file_data.ToString(), Encoding.Unicode );
        }
    }
}

具体来说,该代码将生成一个HTML表单,其中包含BMP中的所有字符,以及以前缀为“r_”的十六进制值命名的输入文本框(r表示“替换值”)。如果将其移植到ASP.NET页面,则可以编写其他代码以尽可能地预先填充替换值:

    如果已经是ASCII,则
  • 具有自己的值,或
  • 使用Unicode规范化的FormD或FormKD分解的等价物,或
  • 整个类别的单个ASCII值(即带有ASCII双引号的所有“标点符号初始”字符)

然后,您可以手动完成并进行调整,这可能不会像您想象的那样长。只有64512个代码点,整个类别的大块可能被解雇为“甚至不接近任何ASCII”。所以,我要构建这个地图和功能。