2012-10-24 32 views
1

我正在使用C#創建一個簡單的iCalendar,並發現RFC 2445的4.1節的內容摺疊令人頭疼(對我來說:-)。iCalendar RFC 2445第4.1節內容摺疊

http://www.apps.ietf.org/rfc/rfc2445.html#sec-4.1

對於長行,你是逃避一些字符(反斜槓,分號,逗號和換行,相信),然後把它摺疊以便沒有線比75個八位字節長。我在網上找到了幾個直接的方法。最簡單的是用轉義版本替換有問題的字符,然後在每第75個字符處插入CRLF。例如:

// too simple, could break at an escape sequence boundary or multi-byte character may overflow 75 octets 
txt = txt.Replace(@"\", "\\\\").Replace(";", "\\;").Replace(",", "\\,").Replace("\r\n", "\\n"); 
var regex = new System.Text.RegularExpressions.Regex(".{75}"); 
var escape_and_folded = regex.Replace(txt, "$0\r\n "); 

我看到兩個問題。有可能將CRLF插入到轉義序列中。例如,如果發生插入,以致轉義的新行序列「\ n」變爲「\ CRLF」(那麼「n」將位於下一行)。第二個問題是何時有多字節字符。由於每個字符的計算可能會超過75個八位字節。

一個簡單的解決方案是逐個字符地移動字符串並退出並摺疊,但這看起來相當殘忍。有人有更優雅的解決方案嗎?

+0

是http://stackoverflow.com/q/10658/1167333解決您的問題? – oberron

回答

2

首先,請確保你看看RFC5545。 RFC2445已過時。 你可以在這裏找到我的PHP實現:

https://github.com/fruux/sabre-vobject/blob/master/lib/Property.php#L252

在PHP中,我們有mb_strcut功能。我不確定是否有.NET的等價物,但這至少會讓事情變得更簡單。到目前爲止,我沒有將摺疊轉義序列(\)減半。一個好的解析器將首先展開這些線條,然後才能解決問題。特別是因爲哪些角色必須逃脫,取決於實際的屬性。 (有時候,;會被轉義,有時它們不會)。

+0

無法訪問該鏈接...看起來不再存在 –

+1

@AndreaAntonangeli已更新 – Evert

1

我試過你的解決方案 - 它的工作原理除了它也摺疊了一些長度小於75個八位字節的行。因此,我重寫了傳統的代碼(即不使用正則表達式 - ,我確實想念他們),如下所示。

public static string FoldLines(this string value, int max, string newline = "\r\n") 
    { 
     var lines = value.Split(new string[]{newline}, System.StringSplitOptions.RemoveEmptyEntries); 
     using (var ms = new System.IO.MemoryStream(value.Length)) 
     { 
      var crlf = Encoding.UTF8.GetBytes(newline); //CRLF 
      var crlfs = Encoding.UTF8.GetBytes(string.Format("{0} ", newline)); //CRLF and SPACE 
      foreach (var line in lines) 
      { 
       var bytes = Encoding.UTF8.GetBytes(line); 
       var len = Encoding.UTF8.GetByteCount(line); 
       if (len <= max) 
       { 
        ms.Write(bytes, 0, len); 
        ms.Write(crlf, 0, crlf.Length); 
       } 
       else 
       { 
        var blen = len/max; //calculate block length 
        var rlen = len % max; //calculate remaining length 
        var b = 0; 
        while (b < blen) 
        { 
         ms.Write(bytes, (b++) * max, max); 
         ms.Write(crlfs, 0, crlfs.Length); 
        } 
        if (rlen > 0) 
        { 
         ms.Write(bytes, blen * max, rlen); 
         ms.Write(crlf, 0, crlf.Length); 
        } 
       } 
      } 

      return Encoding.UTF8.GetString(ms.ToArray()); 
     } 
    } 

  1. 我試圖儘可能要優雅 - 即沒分析該字符串的字符逐但是在八比特組(由最大確定的)的塊。
  2. 該函數最好在所得到的VCALENDAR對象上調用,以便在需要時檢查所有內容行是否進行摺疊和包裝。
  3. 特殊文字的轉義僅在與TEXT相關的屬性(如DESCRIPTION,SUMMARY等)中執行。這些在下面的擴展方法來實現:

    public static string Replace(this string value, IEnumerable<Tuple<string, string>> pairs) 
    { 
        foreach (var pair in pairs) value = value.Replace(pair.Item1, pair.Item2); 
        return value; 
    } 
    
    public static string EscapeStrings(this string value) 
    { 
        return value.Replace(new List<Tuple<string, string>> 
        { 
         new Tuple<string, string>(@"\", "\\\\"), 
         new Tuple<string, string>(";", @"\;"), 
         new Tuple<string, string>(",", @"\,"), 
         new Tuple<string, string>("\r\n", @"\n"), 
        }); 
    } 
    
0

reexmonkey的解決方案在中間寫76個字符摺疊線,因爲它沒有減去多餘的空格字符添加crlfs

我改寫了摺疊功能來糾正這一點,

public static string FoldLines(string value, int max, string newline = "\r\n") 
{ 
    var lines = value.Split(new string[] { newline }, System.StringSplitOptions.RemoveEmptyEntries); 
    using (var ms = new System.IO.MemoryStream(value.Length)) 
    { 
     var crlf = Encoding.UTF8.GetBytes(newline); //CRLF 
     var crlfs = Encoding.UTF8.GetBytes(string.Format("{0} ", newline)); //CRLF and SPACE 
     foreach (var line in lines) 
     { 
      var bytes = Encoding.UTF8.GetBytes(line); 
      var len = Encoding.UTF8.GetByteCount(line); 
      if (len <= max) 
      { 
       ms.Write(bytes, 0, len); 
       ms.Write(crlf, 0, crlf.Length); 
      } 
      else 
      { 
       var offset = 0; //current offset position 
       var count = max; //characters to take 
       while (offset + count < len) 
       { 
        ms.Write(bytes, offset, count); 
        ms.Write(crlfs, 0, crlfs.Length); 
        offset += count; 
        count = max - 1; 
       } 
       count = len - offset; //remaining characters 
       if (count > 0) 
       { 
        ms.Write(bytes, offset, count); 
        ms.Write(crlf, 0, crlf.Length); 
       } 
      } 
     } 

     return Encoding.UTF8.GetString(ms.ToArray()); 
    } 
} 

而且我在EscapeStrings功能增加了額外的元組:

public static string ReplaceText(string value, IEnumerable<Tuple<string, string>> pairs) 
{ 
    foreach (var pair in pairs) value = value.Replace(pair.Item1, pair.Item2); 
    return value; 
} 
public static string EscapeStrings(string value) 
{ 
    return ReplaceText(value, new List <Tuple<string, string>> 
    { 
     new Tuple<string, string>(@"\", "\\\\"), 
     new Tuple<string, string>(";", @"\;"), 
     new Tuple<string, string>(",", @"\,"), 
     new Tuple<string, string>("\r\n", @"\n"), 
     new Tuple<string, string>("\n", @"\n"), 
    }); 
}