【问题标题】:remove 4 byte UTF8 characters删除 4 字节 UTF8 字符
【发布时间】:2016-08-02 07:49:05
【问题描述】:

我想从字符串中删除以 \xF0(ASCII 码为 0xF0 的字符)开头的 4 字节 UTF8 字符并尝试过

sText = Regex.Replace (sText, "\xF0...", "");

这不起作用。使用两个反斜杠也不起作用。

确切的输入是https://de.wikipedia.org/w/index.php?title=Spezial:Exportieren&action=submit&pages=Unicode的内容,4字节字符是文本“[[Violinschlüssel]]”之后的一个字符,十六进制表示:.. 0x65 0x6c 0x5d 0x5d 0x20 0xf0 0x9d 0x84 0x9e 0x20 ..预期输出为 0x65 0x6c 0x5d 0x5d 0x20 0x20 ..

怎么了?

【问题讨论】:

  • 使用两个反斜杠。
  • 可能是因为您试图删除ð character。您的确切输入和确切的预期输出是什么?
  • 评论供我们要求澄清。请点击edit 链接并更新您的帖子,在问题本身中进行澄清。
  • 这是个好问题。它涉及 C# 中字节、字符和字符串之间的非显而易见的关系。
  • 我需要将数据存储在 MySQL 数据库中,使用“UTF8”编码(暂时无法更改)。请看stackoverflow.com/questions/10957238/…

标签: c# regex utf-8


【解决方案1】:

在使用 UTF-16 的 .NET 中,这些字符将是 代理对。它们中的每一个都是两个 UTF-16 代码单元,即两个char 值。

要删除它们,您可以执行 (using System.Linq;):

sText = string.Concat(sText.Where(x => !char.IsSurrogate(x)));

(使用 .NET 4.0 (Visual Studio 2010) 中引入的 Concat 重载)。


后期添加:使用它可能会提供更好的性能:

sText = new string(sText.Where(x => !char.IsSurrogate(x)).ToArray());

即使看起来更糟。 (适用于 .NET 3.5 (Visual Studio 2008)。)

【讨论】:

  • 据我了解,它会删除所有 3 和 4 字节的 UTF8 字符(它们是 C# 字符串中的 2 个 UTF16 字符值)。这不是我所要求的,但我发现这正是我真正需要的。再次感谢。
  • @André 你错了。如果要删除对应于 3 字节 UTF-8 或更长的字符,只需使用 sText = string.Concat(sText.Where(x => x < '\u0800'));。 UTF-8 可以在文件中使用,但一旦string 在内存中,.NET 或 Windows 就不会使用它。如果一个字符在 UTF-8 中需要 1、2 或 3 个字节,则它可以容纳在 UTF-16 中的单个 代码单元(即单个 char 值)中,这是使用的编码.NET 和 Windows 内部。如果一个字符在 UTF-8 中需要 4 个字节,则它需要两个 UTF-16 代码单元(所以 两个 char 值);这两个构成了“代理对”。
【解决方案2】:

您正在尝试搜索 byte 值,但 C# 字符串是由 char 值组成的。 “2.4.4.4 字符文字”部分的 C# 语言规范指出:

字符文字表示单个字符,通常由引号中的字符组成,如 'a'。
...
十六进制转义序列表示单个 Unicode 字符,其值由 \x 后面的十六进制数字组成。

因此搜索"\xF0..." 是搜索字符U+F0,它将由字节C3 B0 表示。

如果要查找替换第一个字节为 0xF0 的所有 Unicode 字符,那么我相信您需要搜索第一个字节为 0xFO 的字符值。

字符U+10000表示为F0 90 80 80(前面的代码是U+FFFF,即EF BF BF)。带有F1 .... .. 的第一个代码是U+40000,即F1 80 80 80,前面的值是U+3FFFF,即F0 BF BF BF

因此您需要删除U+10000U+3FFFF 范围内的字符。这应该可以通过正则表达式来实现,例如

sText = Regex.Replace (sText, "[\\x10000-\\x3FFFF]", "");

问题中引用的来源中的相关字符已提取到下面的代码中。然后代码会尝试了解字符是如何保存在字符串中的。

static void Main(string[] args)
{
    string input = "] ? (";
    Console.Write("Input length  {0} : '{1}'  : ", input.Length, input);
    foreach (char cc in input)
    {
        Console.Write("  {0,2:X02}", (int)cc);
    }
    Console.WriteLine();
}

程序的输出如下。这支持@Jeppe 在他的回答中给出的代理对解释。

Input length  6 : '] ?? ('  :   5D  20  D834  DD1E  20  28 

【讨论】:

  • @Qix 为什么要修改语言标准的直接引用?引用部分没有任何粗体文本,它使用字符串引号。请解释一下。
  • 因为它更好地强调了你的观点。它不会改变规范的含义。我不得不搜索为什么您包含单个字符的符号并且必须搜索 real 答案,这是 unicode char 和单个 byte 之间的区别。
  • @Qix 我回答的第一句话是指C中charbyte的区别。
  • 非常感谢。这很可能指向正确的方向,但您的解决方案仍然不起作用。它从输入中删除了很多字符,但没有删除 4 字节的 UTF8 字符。即使Regex.Replace (sText, "\\x1D11E", "") 也不会从输入中删除精确的单个字符。
猜你喜欢
  • 1970-01-01
  • 2013-05-05
  • 2013-01-10
  • 2011-05-28
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2013-03-04
相关资源
最近更新 更多