返回首页

简介
最近,我开发了一个巨大的ASP.NET页,与30多个控件。大家都知道,这是一个好主意禁用ViewState的控制,实际上并不需要,说文字或标签。这样做后,我发现隐藏的ViewState字段仍然是一个大的数KBS。这显然​​是一个大问题,还没有宽带连接的用户,因为上传40 KB服务器确实是一个不好的问题,特别是当他们开始一而再,再点击"提交"按钮,因为他们不吨通知任何回应。因此,在通过互联网搜索,我构建了一个简单的解决方案,压缩ViewState和因此节省一个粗略的50%的带宽。由Scott Hanselman有特别有用。虽然它可以使用外部库来执行压缩任务,我认为,更好的解决办法是使用了。NET Framework 2.0包括GZipStream或DeflateStream。内存中的数据压缩和解压
首先,我们需要一种方式来压缩和解压的字节数组在内存中。我放在一起这个简单的静态类,它暴露了两个方法:压缩和解压。两个班,GZipStream和DeflateStream,根据使用相同的算法,所以它的无关你选择哪一个。
下面的代码是非常简单的,不需要作进一步的解释:使用System.IO;使用System.IO.Compression;公共静态类压缩机{ 公共静态字节[]压缩(字节[]数据){ MemoryStream的输出=新的MemoryStream(); GZipStream GZIP =新GZipStream(输出,  0; CompressionMode.Compress,TRUE); gzip.Write(数据,0,data.Length); gzip.Close(); 返回output.ToArray(); } 公共静态字节[]解压缩(字节[]数据){ MemoryStream的输入=新的MemoryStream(); input.Write(数据,0,data.Length); input.Position = 0; GZipStream GZIP =新GZipStream(输入,   ; CompressionMode.Decompress,TRUE); MemoryStream的输出=新的MemoryStream(); byte []的BUFF =新的字节[64]; INT读= -1; 60; 读= gzip.Read(BUFF,0,buff.Length); (阅读GT; 0){ output.Write(BUFF,0,读); & #160; 读= gzip.Read(BUFF,0,buff.Length); }  ; gzip.Close(); 返回output.ToArray(); }}
你需要保存这个类。cs文件,并把它在你的ASP.NET应用程序的App_Code目录,确保它包含在适当的自定义命名空间(如果你不指定任何命名空间,类内置的ASP命名空间)。压缩的ViewState
现在,我们实际上可以压缩页面的ViewState。要做到这一点,我们必须重写两种方法LoadPageStateFromPersistenceMedium和SavePageStateToPersistenceMedium。简单的代码使用一个额外的隐藏字段,__VSTATE,存储压缩的ViewState。正如你可以看到,通过查看页面的HTML,__VIEWSTATE字段是空的,而我们的__VSTATE领域包含压缩的ViewState,Base64编码。让我们来看看code.public部分类的页面"的System.Web.UI.Page { 保护的覆盖对象LoadPageStateFromPersistenceMedium(){ 字符串的ViewState的Request.Form ["__VSTATEquot;]; 字节[]字节= Convert.FromBase64String(视图状态); 字节= Compressor.Decompress(字节); LosFormatter格式化=新LosFormatter();   ; 返回formatter.Deserialize(Convert.ToBase64String(字节数)); } 保护覆盖无效SavePageStateToPersistenceMedium(对象的ViewState){ LosFormatter格式化=新LosFormatter(); StringWriter作家=新StringWriter();  ; formatter.Serialize(作家ViewState的); 字符串viewStateString = writer.ToString(); 字节[]字节= Convert.FromBase64String(viewStateString); 字节= Compressor.Compress(字节); ClientScript.RegisterHiddenField("; __VSTATEquot";,Convert.ToBase64String(字节));   ;} / /你的代码在这里休息... ...}
在第一种方法,我们只是从Base64的解码,解压缩和反序列化的__VSTATE内容,并返回它运行时。在第二种方法中,我们执行相反的操作:序列化,压缩,并以base64编码。的base64字符串,然后保存到__VSTATE隐藏字段。 LosFormatter对象进行序列化和反序列化的任务。
您可能还需要创建一个新类,例如,CompressedPage,从System.Web.UI.Page继承,在其中覆盖两种方法,然后从这个类继承您的网页,例如页面",:CompressedPage。只要记住。NET中只有单继承,并通过这种方式,"花"你唯一的继承机会使用ViewState压缩。另一方面,在每一个类的两个方法是压倒一切的是浪费时间,所以你要选择最适合您需要的方式,。表演和结论
经过几次试验,我发现从38 KB到17 KB的ViewState已减少,节省了44%。假如你有一个平均用户每分钟1回传,可以为您节省每一个用户每月的带宽超过885 MB。这是一个很好的的结果:您节省带宽(和钱),和用户通知服务器的响应时间较短的。
我想指出,这种解决方案对服务器的硬件性能命中。压缩,解压缩,编码,解码数据服务器是一个相当繁重的工作,所以你必须平衡你的CPU能力和内存的用户数量。

回答

评论会员:lunabi66 时间:2011/12/06
它为我节省了很多时间
!谢谢!
路易莎:

| jrbosch
评论会员:!非常好的 时间:2011/12/06
ShadowDanser
评论会员:游客 时间:2011/12/06
这是在我的应用程序中的ViewState的结果:用你的例子:H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9fB8/Iv7vX/M3/e1zV/3F/zv/9JtWzrqmxe5b9oXdT5y6ppn2TTt79Xfv37//6/za/56/7YyTyfvn1Svdv9TX/dH/v03sHuzt6Dg3uz/wcl/rgoPwAAAA=="不使用你的榜样,相同的WebForm:="/wEPDwUJNjM4MTAyNzgzZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUJQ2hlY2tCb3gxeC4oWoGKhZjCjTZmpgxFgH6MxQ=¿我做错了什么
?truongpham
评论会员:游客 时间:2011/12/06
我有同样的结果:我的Viewstate更大的压缩比没有
inharry
评论会员:游客 时间:2011/12/06
我来到过这篇文章。这是一个伟大的文章。感谢分享我试图与样品有点发挥。我发现,ViewState是真的压缩,如果数据足够长的时间。有多次ViewState数据的大小是压缩后的成长。对于短数据,压缩效率。这里是我的建议,为优化codesnippets。首先,我们之前插入一个头(1字节大小)的第一个字节的ViewState。这个头告诉ViewState数据是否被压缩或不保护覆盖无效SavePageStateToPersistenceMedium(对象的ViewState){ ...字节=Compressor.Compress(字节);//新CODESNIPPET:byte[]的TMP=Compressor.Compress(字节);大小//如果是真的比原来压缩后,压缩阵列是可取 如果(tmp.Length目的:延迟程序执行,直到指定的条件是指
Maha_M
评论会员:游客 时间:2011/12/06
!嗨(请原谅我的英语水平)。代码,工程99%OK!也改变了像一些海报ScriptManager.RegisterHiddenField建议。但我UserControls的一些问题。ASCX里面的UpdatePanel。我的UserControls下拉组合,我注意到,组合dissaperar包含。任何意见?在此先感谢。最好的问候,奥梅罗冈萨雷斯
。lrwilson
评论会员:游客 时间:2011/12/06
其实ViewState的,它是一个伟大的工具开发Web应用程序的。但它可以是一个性能故障,如果它长得太多,你的文章,它的顺利解决办法。感谢分享也减少了越来越可怕的MAC问题的机会。大
达里奥索莱拉
评论会员:游客 时间:2011/12/06
gzip头神奇的数字是不正确的。确保你是在传递一个gzip流我得到这个错误,而在这条线读=gzip.Read(BUFF,0,buff.Length)请帮助我。Maharajan,软件工程师,Kom7技术,印度..
mdmasonmbcs
评论会员:游客 时间:2011/12/06
这是一个伟大的的解决方案。我很多使用ViewState,即使我禁用它在那里我可以,我仍然得到的ViewState比我想的。考虑这一修改:公共静态类压缩机{公共静态字节[]压缩(字节[]数据){ (MemoryStream的输出=新的MemoryStream()){ (GZipStreamGZIP=新GZipStream(输出,CompressionMode.Compress,真实)) { gzip.Write(数据,0,data.Length) gzip.Close() 返回output.ToArray() } }} 公共静态字节[]解压缩(字节[]数据) {(MemoryStream的输入=新的MemoryStream()) {input.Write(数据,0,data.Length) input.Position=0; (GZipStreamGZIP=新GZipStream(输入,CompressionMode.Decompress,TRUE)) { (MemoryStream的输出=新的MemoryStream()) { 字节[]BUFF=新的字节[64] INT读=-1; 读=gzip.Read(BUFF,0,buff.Length) (阅读GT;0) { output.Write(BUFF,0,读);读=gzip.Read(BUFF,0,buff.Length) } gzip.Close()返回output.ToArray() } }}}}正是像以前一样,但因为所有的对象使用IDisposable的,我开始"()"。这里的原因:imgsrc=http://www.orcode.com/upimg/2011_12_06_18_35_38_0.gif我想有一个与原代码内存泄漏的危险,因为对象是不出售的。你觉得呢?
添麦柯迪
评论会员:游客 时间:2011/12/06
考虑,我在这里使用的流总是在RAM管理,我应该使用可避免使用,因为对象是在其范围的结束(我希望)处置。无论如何,它肯定使用使用一个很好的做法。如果你真的相信你需要拿起手机说:"东西"关于你的个性,不要打扰。您没有个性。精神病,也许-但不是个性。-{A}imgsrc=http://www.orcode.com/upimg/2011_12_06_18_35_38_2.gif
Mninawa
评论会员:游客 时间:2011/12/06
好文章!伟大的想法!我已尝试使用ASPNET2.0与1.0的AJAXWeb应用程序,发现我得到一个选择网格行之后的ObjectDisposedException。注册隐藏字段,我使用了正确的ScriptManager参考。出于某种原因,过早关闭流。堆栈跟踪在System.IO.__Error.StreamIsClosed()System.IO.MemoryStream.Read(byte[]的缓冲区,INT32,INT32抵消计数)在System.IO.Compression.DeflateStream.Read(byte[]数组,偏移INT32,INT32计数)在System.IO.Compression.GZipStream.Read(byte[]数组,偏移INT32,INT32计数)在DecompressViewState错误发生在ViewStateCompressor.cs是静态的byte[]DecompressViewState(字节[]compData){.....{BR}byteRead=gzip.Read(BUF,0,buf.Length).....{BR}}我想,感到异常的原因。
添麦柯迪
评论会员:游客 时间:2011/12/06
这是因为你还没有考虑"AsyncPostBacks"。在每次回发时,你需要压缩/解压缩的ViewState。这需要特殊处理,在MSAJAX涉及AsyncPostBack。如果你正在使用differt的Ajax框架,对不起,我不能帮你,但它是同一个概念。您不需要修改您压缩/解压缩例程,刚刚恢复/保存PageStateToPersistanceMedium请注意在粗体文本"SavePageStateToPersistenceMedium"的方法。在AsyncPostBack您需要重新注册HiddenField,但是,你不能只是简单地这样做同样的方式。如果你在AsyncPostBack的时候,你需要使用ScriptManager的方法来做到这一点。如果你只是在第一pageLoad的或后续的回传,然后使用的ClientScript罚款。codeprespanclass="code-SummaryComment"////spanspanclass="code-comment"spanclass="code-SummaryComment"</spanspanclass="code-SummaryComment"summary/spanspanclass="code-SummaryComment">/span/spanspanclass="code-SummaryComment"////spanspanclass="code-comment"LoadstheViewStatefromthepersistencemedium/spanspanclass="code-SummaryComment"////spanspanclass="code-comment"spanclass="code-SummaryComment"</spanspanclass="code-SummaryComment"//spanspanclass="code-SummaryComment"summary/spanspanclass="code-SummaryComment">/span/spanspanclass="code-keyword"protected/spanspanclass="code-keyword"override/spanspanclass="code-keyword"object/spanLoadPageStateFromPersistenceMedium(){ spanclass="code-keyword"try/span{ spanclass="code-keyword"string/spancompression=ConfigurationManager.AppSettings[spanclass="code-string""/spanspanclass="code-string"viewStateCompression"/span]; spanclass="code-keyword"if/span(spanclass="code-keyword"string/span.IsNullOrEmpty(compression))compression=spanclass="code-string""/spanspanclass="code-string"true"/span; spanclass="code-keyword"if/span(spanclass="code-keyword"bool/span.Parse(compression)){ spanclass="code-keyword"string/spanviewState=Request.Form[spanclass="code-string""/spanspanclass="code-string"__VSTATE"/span]; spanclass="code-keyword"if/span(viewState.EndsWith(spanclass="code-string""/spanspanclass="code-string","/span))viewState=viewState.Substring(spanclass="code-digit"0/span,viewState.Length-spanclass="code-digit"1/span); spanclass="code-keyword"byte/span[]bytes=Convert.FromBase64String(viewState); bytes=BasePage.Decompress(bytes);  viewState=Convert.ToBase64String(bytes); spanclass="code-keyword"if/span(spanclass="code-keyword"string/span.IsNullOrEmpty(viewState))spanclass="code-keyword"return/spanspanclass="code-keyword"null/span; LosFormatterformatter=spanclass="code-keyword"new/spanLosFormatter(); spanclass="code-keyword"return/spanformatter.Deserialize(viewState); } spanclass="code-keyword"else/span spanclass="code-keyword"return/spanspanclass="code-keyword"base/span.LoadPageStateFromPersistenceMedium(); } spanclass="code-keyword"catch/span(Exception){spanclass="code-keyword"throw/span;}} spanclass="code-SummaryComment"////spanspanclass="code-comment"spanclass="code-SummaryComment"</spanspanclass="code-SummaryComment"summary/spanspanclass="code-SummaryComment">/span/spanspanclass="code-SummaryComment"////spanspanclass="code-comment"SavestheViewStatefromthepersistencemedium/spanspanclass="code-SummaryComment"////spanspanclass="code-comment"spanclass="code-SummaryComment"</spanspanclass="code-SummaryComment"//spanspanclass="code-SummaryComment"summary/spanspanclass="code-SummaryComment">/span/spanspanclass="code-keyword"protected/spanspanclass="code-keyword"override/spanspanclass="code-keyword"void/spanSavePageStateToPersistenceMedium(spanclass="code-keyword"object/spanstate){ StringWriterwriter=spanclass="code-keyword"new/spanStringWriter(); spanclass="code-keyword"try/span{ spanclass="code-keyword"string/spancompression=ConfigurationManager.AppSettings[spanclass="code-string""/spanspanclass="code-string"viewStateCompression"/span]; spanclass="code-keyword"if/span(spanclass="code-keyword"string/span.IsNullOrEmpty(compression))compression=spanclass="code-string""/spanspanclass="code-string"true"/span; spanclass="code-keyword"if/span(spanclass="code-keyword"bool/span.Parse(compression)){ LosFormatterformatter=spanclass="code-keyword"new/spanLosFormatter(); formatter.Serialize(writer,state); spanclass="code-keyword"string/spanvState=writer.ToString(); spanclass="code-keyword"byte/span[]bytes=Convert.FromBase64String(vState); bytes=BasePage.Compress(bytes); vState=Convert.ToBase64String(bytes);  bSystem.Web.UI.ScriptManagersm=System.Web.UI.ScriptManager.GetCurrent(spanclass="code-keyword"this/span); spanclass="code-keyword"if/span(sm!=spanclass="code-keyword"null/span&&sm.IsInAsyncPostBack) System.Web.UI.ScriptManager.RegisterHiddenField(spanclass="code-keyword"this/span,spanclass="code-string""/spanspanclass="code-string"__VSTATE"/span,vState); spanclass="code-keyword"else/span Page.ClientScript.RegisterHiddenField(spanclass="code-string""/spanspanclass="code-string"__VSTATE"/span,vState);/b } spanclass="code-keyword"else/span spanclass="code-keyword"base/span.SavePageStateToPersistenceMedium(state); } spanclass="code-keyword"catch/span(Exception){spanclass="code-keyword"throw/span;} spanclass="code-keyword"finally/span{ spanclass="code-keyword"if/span(writer!=spanclass="code-keyword"null/span){writer.Dispose();writer=spanclass="code-keyword"null/span;} }}/pre/code
mrkyle
评论会员:游客 时间:2011/12/06
今天你救了我的屁股,你的代码完美,我没有考虑AJAX的一部分..IM使用radcontrol启动AJAX在我的应用程序。从M.佩奇
Sujith约翰托马斯
评论会员:游客 时间:2011/12/06
很乐意帮助。我有一个小的更新。有没有需要调用Page.ClientScript的,如果你有一个ScriptManager,因为内部知道,如果是在一个AsyncPostBack使用的ClientScript内部如果需要的话,所以你可以改变所有的代码,这:{C}(通知,我不检查,如果当前请求是异步):System.Web.UI.ScriptManager.RegisterHiddenField(...);{BR}或System.Web.UI.ScriptManager.RegisterScriptBlock(...);{BR}或System.Web.UI.ScriptManager.RegisterScript(...);{BR}
boros24
评论会员:游客 时间:2011/12/06
可以这样做隐藏字段__EVENTVALIDATION也?
Munsifali拉希德
评论会员:游客 时间:2011/12/06
任何其他已知的与这个TECHNIC问题?
法赫德Azeem
评论会员:游客 时间:2011/12/06
嗨,漂亮的代码我用它,但我有一些问题,(一些控制失去了视图状态)。我发现一个更好的方法适用于同样的想法:创建一个新类的PageStatePersister继承覆盖的页面的PageStatePersister属性。这里是我的类:codeprespanclass="code-keyword"Public/spanspanclass="code-keyword"Class/spanVSCompressorspanclass="code-keyword"Inherits/spanPageStatePersisterspanclass="code-keyword"Private/span_stateFormatterspanclass="code-keyword"As/spanLosFormatterspanclass="code-keyword"Protected/spanspanclass="code-keyword"Shadows/spanspanclass="code-keyword"ReadOnly/spanspanclass="code-keyword"Property/spanStateFormatter()spanclass="code-keyword"As/spanLosFormatterspanclass="code-keyword"Get/spanspanclass="code-keyword"If/spanspanclass="code-keyword"Me/span._stateFormatterspanclass="code-keyword"Is/spanspanclass="code-keyword"Nothing/spanspanclass="code-keyword"Then/spanspanclass="code-keyword"Me/span._stateFormatter=spanclass="code-keyword"New/spanLosFormatterspanclass="code-keyword"End/spanspanclass="code-keyword"If/spanspanclass="code-keyword"Return/spanspanclass="code-keyword"Me/span._stateFormatterspanclass="code-keyword"End/spanspanclass="code-keyword"Get/spanspanclass="code-keyword"End/spanspanclass="code-keyword"Property/spanspanclass="code-keyword"Public/spanspanclass="code-keyword"Sub/spanspanclass="code-keyword"New/span(spanclass="code-keyword"ByVal/spanpagespanclass="code-keyword"As/spanPage)spanclass="code-keyword"MyBase/span.spanclass="code-keyword"New/span(page)spanclass="code-keyword"End/spanspanclass="code-keyword"Sub/spanspanclass="code-keyword"Public/spanspanclass="code-keyword"Overrides/spanspanclass="code-keyword"Sub/spanLoad()spanclass="code-keyword"Dim/spanbytes()spanclass="code-keyword"As/spanspanclass="code-keyword"Byte/span=Convert.FromBase64String(spanclass="code-keyword"MyBase/span.Page.Request.Form(spanclass="code-string""/spanspanclass="code-string"__VSTATE"/span))spanclass="code-keyword"Dim/spaninputspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanMemoryStreaminput.Write(bytes,spanclass="code-digit"0/span,bytes.Length)input.Position=spanclass="code-digit"0/spanspanclass="code-keyword"Dim/spangzipspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanGZipStream(input,CompressionMode.Decompress,spanclass="code-keyword"True/span)spanclass="code-keyword"Dim/spanoutputspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanMemoryStreamspanclass="code-keyword"Dim/spanbuff(spanclass="code-digit"64/span)spanclass="code-keyword"As/spanspanclass="code-keyword"Byte/spanspanclass="code-keyword"Dim/spanreadspanclass="code-keyword"As/spanspanclass="code-keyword"Integer/span=-1read=gzip.Read(buff,spanclass="code-digit"0/span,buff.Length)spanclass="code-keyword"While/spanreadspanclass="code-digit"0/spanoutput.Write(buff,spanclass="code-digit"0/span,read)read=gzip.Read(buff,spanclass="code-digit"0/span,buff.Length)spanclass="code-keyword"End/spanspanclass="code-keyword"While/spangzip.Close()gzip.Dispose()spanclass="code-keyword"Dim/spanpspanclass="code-keyword"As/spanPair=spanclass="code-keyword"DirectCast/span(StateFormatter.Deserialize(Convert.ToBase64String(output.ToArray)),Pair)spanclass="code-keyword"MyBase/span.ViewState=p.Firstspanclass="code-keyword"MyBase/span.ControlState=p.Secondspanclass="code-keyword"End/spanspanclass="code-keyword"Sub/spanspanclass="code-keyword"Public/spanspanclass="code-keyword"Overrides/spanspanclass="code-keyword"Sub/spanSave()spanclass="code-keyword"Dim/spanwriterspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanStringWriterStateFormatter.Serialize(writer,spanclass="code-keyword"New/spanPair(spanclass="code-keyword"MyBase/span.ViewState,spanclass="code-keyword"MyBase/span.ControlState))spanclass="code-keyword"Dim/spanbytes()spanclass="code-keyword"As/spanspanclass="code-keyword"Byte/span=Convert.FromBase64String(writer.ToString)spanclass="code-keyword"Dim/spanoutputspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanMemoryStreamspanclass="code-keyword"Dim/spangzipspanclass="code-keyword"As/spanspanclass="code-keyword"New/spanGZipStream(output,CompressionMode.Compress,spanclass="code-keyword"True/span)gzip.Write(bytes,spanclass="code-digit"0/span,bytes.Length)gzip.Close()gzip.Dispose()spanclass="code-keyword"MyBase/span.Page.ClientScript.RegisterHiddenField(spanclass="code-string""/spanspanclass="code-string"__VSTATE"/span,Convert.ToBase64String(output.ToArray))spanclass="code-keyword"End/spanspanclass="code-keyword"Sub/spanspanclass="code-keyword"End/spanspanclass="code-keyword"Class/span/pre/code这是你应该把在页面的代码:codeprePrivate_persisterAsNewVSCompressor(Me)ProtectedOverridesReadOnlyPropertyPageStatePersister()AsPageStatePersisterGetReturn_persisterEndGetEndProperty/pre/codeING它原来的大小是900KB压缩与您的代码后是300KB,现在是150KB
环视
评论会员:游客 时间:2011/12/06
伟大的解决方案。正常,只是与AJAX回传的工作,请更改以下行(另一个海报的建议):MyBase.Page.ClientScript.RegisterHiddenField("__VSTATE",Convert.ToBase64String(output.ToArray))有了这样的:ScriptManager.RegisterHiddenField(页,"__VSTATE",Convert.ToBase64String(output.ToArray))否则,视图状态没有得到更新,并可能导致一些很奇怪的行为(因为我学到了硬!)欢呼声,屯门-Munsifali拉希德imgsrc=http://www.orcode.com/upimg/2011_12_06_18_35_38_3.gif
DAJG:像500 KB至1.18 MB |我网页上的ViewState是巨大的。这种方法减少了很多。
良好的工作! {S0}
评论会员:uyildir 时间:2011/12/06
!很好的解决方案
使用AJAX 1.0的压缩,有一个问题,因为ViewState是在部分呈现不更新(使用UpdatePanel)。
解决的办法是更换线路
ClientScript.RegisterHiddenField("__VSTATE",Convert.ToBase64String(字节))
这样的代码:
ScriptManager.RegisterHiddenField(这一点,"__VSTATE",Convert.ToBase64String(字节))

有了这个简单的变化,所有工程与阿贾克斯也不错... ...
"
评论会员:CoolVini 时间:2011/12/06
感谢,它的工作原理就像一个魅力{S2}

D @ JG
评论会员:kjerolran 时间:2011/12/06
你救了我的命!我去的控制和视图状态问题疯狂。非常感谢您! {S0}