C# 有没有这样一种东西,比如;“用户定义的编码回退”;

C# 有没有这样一种东西,比如;“用户定义的编码回退”;,c#,encoding,ascii,fallback,C#,Encoding,Ascii,Fallback,当使用ASCII编码并将字符串编码为字节时,像ö这样的字符将产生? Encoding encoding = Encoding.GetEncoding("us-ascii"); // or Encoding encoding = Encoding.ASCI; data = encoding.GetBytes(s); 我正在寻找用不同的字符替换这些字符的方法,而不仅仅是问号。 示例: ä -> ae ö -> oe ü -> ue ß -> ss 如果不能将一个字

当使用ASCII编码并将字符串编码为字节时,像
ö
这样的字符将产生

Encoding encoding = Encoding.GetEncoding("us-ascii");     // or Encoding encoding = Encoding.ASCI;
data = encoding.GetBytes(s);
我正在寻找用不同的字符替换这些字符的方法,而不仅仅是问号。
示例:

ä -> ae
ö -> oe
ü -> ue
ß -> ss
如果不能将一个字符替换为多个字符,我将接受是否可以将它们替换为一个字符(
o
->
o

现在有几种
EncoderFallback
的实现,但我不明白它们是如何工作的。
一个快速而肮脏的解决方案是在将字符串赋给
Encoding.GetBytes()
之前替换所有这些字符,但这似乎不是“正确”的方法。
我希望我能给编码对象一个替换表

我怎样才能做到这一点

实现您想要的“最正确”的方法是实现一个定制的回退编码器,该编码器可以实现最佳的回退。由于各种原因,.NET中内置的一个在它将尝试最适合的字符方面是相当保守的(存在安全隐患,这取决于您计划使用什么来放置重新编码的字符串。)您的自定义回退策略可以根据您想要的任何规则进行最佳匹配

话虽如此,在您的回退类中,您最终将编写一个包含所有不可编码的Unicode代码点的巨型case语句,并手动将它们映射到最适合的备选方案。通过提前循环字符串并将不支持的字符替换为替换字符,可以实现相同的目标。回退策略的主要好处是性能:您只能在字符串中循环一次,而不是至少两次。除非你的关系很大,否则我不会太担心

如果您确实想要实现自定义的回退策略,您一定要阅读我的评论中的文章:。这并不难,但您必须了解编码回退是如何工作的

您提供了
Encoder.GetEncoding
方法作为自定义类的实现,该类必须派生自
EncoderFallback
。不过,该类基本上只是实际工作的包装,这是在
EncoderFallbackBuffer
中完成的。您需要缓冲区的原因是,回退不一定是一对一的过程;在您的示例中,您可能最终将一个Unicode字符映射为两个ASCII字符

当编码过程第一次遇到问题并需要依赖于您的策略时,它使用
EncoderFallback
实现来创建
EncoderFallbackBuffer
的实例。然后调用自定义缓冲区的
Fallback
方法

在内部,缓冲区建立了一组要返回的字符,以代替不可编码的字符,并返回
true
。从那里,编码器将重复调用
GetNextChar
,只要
剩余>0
和/或直到
GetNextChar
返回CP 0,并将这些字符粘贴到编码结果中

这篇文章包含了一个实现,它非常准确地描述了您要做的事情;我已经复制了下面的基本框架,应该可以让您开始了

public class CustomMapper : EncoderFallback
{
   // Use can override the "replacement character", so track what they
   // give us.
   public string DefaultString;

   public CustomMapper() : this("*")
   {   
   }

   public CustomMapper(string defaultString)
   {
      this.DefaultString = defaultString;
   }

   public override EncoderFallbackBuffer CreateFallbackBuffer()
   {
      return new CustomMapperFallbackBuffer(this);
   }

   // This is the length of the largest possible replacement string we can
   // return for a single Unicode code point.
   public override int MaxCharCount
   {
      get { return 2; }
   } 
}

public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
   CustomMapper fb; 

   public CustomMapperFallbackBuffer(CustomMapper fallback)
   {
      // We can use the same custom buffer with different fallbacks, e.g.
      // we might have different sets of replacement characters for different
      // cases. This is just a reference to the parent in case we want it.
      this.fb = fallback;
   }

   public override bool Fallback(char charUnknown, int index)
   {
      // Do the work of figuring out what sequence of characters should replace
      // charUnknown. index is the position in the original string of this character,
      // in case that's relevant.

      // If we end up generating a sequence of replacement characters, return
      // true, and the encoder will start calling GetNextChar. Otherwise return
      // false.

      // Alternatively, instead of returning false, you can simply extract
      // DefaultString from this.fb and return that for failure cases.
   }

   public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
   {
      // Same as above, except we have a UTF-16 surrogate pair. Same rules
      // apply: if we can map this pair, return true, otherwise return false.
      // Most likely, you're going to return false here for an ASCII-type
      // encoding.
   }

   public override char GetNextChar()
   {
      // Return the next character in our internal buffer of replacement
      // characters waiting to be put into the encoded byte stream. If
      // we're all out of characters, return '\u0000'.
   }

   public override bool MovePrevious()
   {
      // Back up to the previous character we returned and get ready
      // to return it again. If that's possible, return true; if that's
      // not possible (e.g. we have no previous character) return false;
   }

   public override int Remaining 
   {
      // Return the number of characters that we've got waiting
      // for the encoder to read.
      get { return count < 0 ? 0 : count; }
   }

   public override void Reset()
   {
       // Reset our internal state back to the initial one.
   }
}
公共类CustomMapper:encoderCallback
{
//使用可以覆盖“替换字符”,以便跟踪它们的内容
//给我们。
公共字符串DefaultString;
公共CustomMapper():此(“*”)
{   
}
公共CustomMapper(字符串defaultString)
{
this.DefaultString=DefaultString;
}
公共重写编码器FallBackBuffer CreateFallbackBuffer()
{
返回新的CustomMapperFallbackBuffer(此);
}
//这是我们可以替换的最大字符串的长度
//返回单个Unicode代码点。
公共覆盖整型MaxCharCount
{
获取{return 2;}
} 
}
公共类CustomMapperFallbackBuffer:EncoderFallbackBuffer
{
CustomMapper fb;
公共CustomMapperFallbackBuffer(CustomMapper回退)
{
//我们可以将相同的自定义缓冲区用于不同的回退,例如。
//我们可能有不同的替换字符集用于不同的应用程序
//案例。这只是一个对父级的引用,以防我们需要它。
this.fb=回退;
}
公共覆盖布尔回退(char charUnknown,int索引)
{
//做一些工作,找出应该替换的字符序列
//charUnknown.index是此字符在原始字符串中的位置,
//以防万一。
//如果我们最终生成一系列替换字符,返回
//则编码器将开始调用GetNextChar。否则返回
//错。
//或者,不返回false,只需提取
//来自this.fb的DefaultString,并在失败情况下返回该字符串。
}
公共覆盖布尔回退(char charUnknownHigh、char charUnknownLow、int index)
{
//与上面相同,只是我们有一个UTF-16代理项对。规则相同
//应用:如果我们可以映射这对,则返回true,否则返回false。
//对于ASCII类型,很可能会在此处返回false
//编码。
}
public override char GetNextChar()
{
//返回替换的内部缓冲区中的下一个字符
//等待放入编码字节流的字符。如果
//我们都没有字符了,返回'\u0000'。
}
公共覆盖bool MovePrevious()
{
//回到我们返回的前一个角色并做好准备
//再次返回。如果可能,返回true;如果是
//不可能(例如,我们没有以前的字符)返回false;
}
公共覆盖剩余整数
{
//返回等待的字符数
//用于编码器读取。
获取{r