मेरे पास C # में 3 बाइट सरणियाँ हैं जिन्हें मुझे एक में संयोजित करने की आवश्यकता है। इस कार्य को पूरा करने के लिए सबसे प्रभावी तरीका क्या होगा?
मेरे पास C # में 3 बाइट सरणियाँ हैं जिन्हें मुझे एक में संयोजित करने की आवश्यकता है। इस कार्य को पूरा करने के लिए सबसे प्रभावी तरीका क्या होगा?
जवाबों:
आदिम प्रकारों के लिए (बाइट्स सहित), के System.Buffer.BlockCopy
बजाय का उपयोग करें System.Array.Copy
। यह तेज है।
मैंने सुझाए गए तरीकों में से प्रत्येक को 10 बाइट्स के 3 सरणियों का उपयोग करते हुए 1 मिलियन बार लूप में निष्पादित किया है। यहाँ परिणाम हैं:
System.Array.Copy
- 0.2187556 सेकंडSystem.Buffer.BlockCopy
- 0.1406286 सेकंडमैंने प्रत्येक सरणी का आकार 100 तत्वों तक बढ़ाया और परीक्षण फिर से चलाया:
System.Array.Copy
- 0.2812554 सेकंडSystem.Buffer.BlockCopy
- 0.2500048 सेकंडमैंने प्रत्येक सरणी का आकार 1000 तत्वों तक बढ़ाया और परीक्षण फिर से चलाया:
System.Array.Copy
- 1.0781457 सेकंडSystem.Buffer.BlockCopy
- 1.0156445 सेकंडअंत में, मैंने प्रत्येक सरणी के आकार को 1 मिलियन तत्वों तक बढ़ाया और परीक्षण को फिर से चलाया, प्रत्येक लूप को केवल 4000 बार निष्पादित किया :
System.Array.Copy
- 13.4533833 सेकंडSystem.Buffer.BlockCopy
- 13.1096267 सेकंडइसलिए, यदि आपको एक नई बाइट सरणी की आवश्यकता है, तो उपयोग करें
byte[] rv = new byte[a1.Length + a2.Length + a3.Length];
System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length);
System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length);
System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length);
लेकिन, यदि आप एक का उपयोग कर सकते हैं IEnumerable<byte>
, निश्चित रूप से LINQ के कॉनैट <> विधि को पसंद करते हैं। यह सी # उपज ऑपरेटर की तुलना में केवल थोड़ा धीमा है, लेकिन अधिक संक्षिप्त और अधिक सुरुचिपूर्ण है।
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
यदि आपके पास बकाया संख्या है और .NET 3.5 का उपयोग कर रहे हैं, तो आप System.Buffer.BlockCopy
समाधान को इस तरह अधिक सामान्य बना सकते हैं :
private byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays) {
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
* नोट: उपरोक्त ब्लॉक आपको काम करने के लिए शीर्ष पर निम्नलिखित नाम स्थान को जोड़ने की आवश्यकता है।
using System.Linq;
बाद के डेटा संरचनाओं (बाइट सरणी बनाम IEnumerable <बाइट>) की पुनरावृत्ति के बारे में जॉन स्कीट की बात करने के लिए, मैंने पिछली बार परीक्षण (1 मिलियन तत्व, 4000 पुनरावृत्तियों) को फिर से चलाया, एक लूप जोड़कर जो प्रत्येक के साथ पूर्ण सरणी पर पुनरावृत्ति करता है उत्तीर्ण करना:
System.Array.Copy
- 78.20550510 सेकंडSystem.Buffer.BlockCopy
- 77.89261900 सेकंडमुद्दा यह है कि निर्माण और परिणामी डेटा संरचना के उपयोग दोनों की दक्षता को समझना बहुत महत्वपूर्ण है । बस सृजन की दक्षता पर ध्यान केंद्रित करने से उपयोग से जुड़ी अक्षमता को नजरअंदाज किया जा सकता है। कुडोस, जॉन।
उत्तर में से कई मुझे बताई गई आवश्यकताओं की अनदेखी करते हैं:
ये दोनों एक साथ बाइट्स के एक LINQ अनुक्रम को नियंत्रित करते हैं - कुछ भी जिसके साथ yield
पूरे अनुक्रम के माध्यम से पुनरावृत्ति के बिना अंतिम आकार प्राप्त करना असंभव है।
यदि वे पाठ्यक्रम की वास्तविक आवश्यकताएं नहीं हैं , तो LINQ पूरी तरह से एक अच्छा समाधान (या IList<T>
कार्यान्वयन) हो सकता है। हालाँकि, मुझे लगता है कि सुपरडुंबेल जानता है कि वह क्या चाहता है।
(संपादित करें: मैंने अभी एक और विचार किया है। सरणियों की एक प्रति बनाने और उन्हें आलसी रूप से पढ़ने के बीच एक बड़ा अर्थपूर्ण अंतर है। विचार करें कि क्या होता है यदि आप कॉल करने के बाद "स्रोत" सरणियों में से एक में डेटा बदलते हैं Combine
(या जो भी हो ) विधि लेकिन परिणाम का उपयोग करने से पहले - आलसी मूल्यांकन के साथ, वह परिवर्तन दिखाई देगा। तत्काल प्रतिलिपि के साथ, यह अलग-अलग स्थितियों में अलग-अलग व्यवहार के लिए कॉल नहीं करेगा - बस कुछ के बारे में पता होना चाहिए।)
यहाँ मेरी प्रस्तावित विधियाँ हैं - जो कि कुछ अन्य उत्तरों में निहित हैं, निश्चित ही :)
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] Combine(byte[] first, byte[] second, byte[] third)
{
byte[] ret = new byte[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
बेशक "परमेस" संस्करण को पहले बाइट सरणियों की एक सरणी बनाने की आवश्यकता होती है, जो अतिरिक्त अक्षमता का परिचय देती है।
मैंने कोड स्वच्छता के लिए एक कदम आगे मैट का LINQ उदाहरण लिया:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
मेरे मामले में, सरणियाँ छोटी हैं, इसलिए मुझे प्रदर्शन के बारे में चिंता नहीं है।
यदि आपको एक नई बाइट सरणी की आवश्यकता है, तो निम्नलिखित का उपयोग करें:
byte[] Combine(byte[] a1, byte[] a2, byte[] a3)
{
byte[] ret = new byte[a1.Length + a2.Length + a3.Length];
Array.Copy(a1, 0, ret, 0, a1.Length);
Array.Copy(a2, 0, ret, a1.Length, a2.Length);
Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length);
return ret;
}
वैकल्पिक रूप से, यदि आपको केवल एक IEnumerable की आवश्यकता है, तो C # 2.0 उपज ऑपरेटर का उपयोग करने पर विचार करें:
IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3)
{
foreach (byte b in a1)
yield return b;
foreach (byte b in a2)
yield return b;
foreach (byte b in a3)
yield return b;
}
मैं वास्तव में कॉनैट का उपयोग करके कुछ मुद्दों में भाग गया ... (10 मिलियन में सरणियों के साथ, यह वास्तव में दुर्घटनाग्रस्त हो गया)।
मैंने पाया कि सरल, आसान और मेरे ऊपर दुर्घटनाग्रस्त हुए बिना अच्छी तरह से काम करता है, और यह किसी भी संख्या में सरणियों के लिए काम करता है (सिर्फ तीन नहीं) (यह LINQ का उपयोग करता है):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
मेमोरीस्ट्रीम क्लास यह काम मेरे लिए बहुत अच्छी तरह से करता है। मैं बफर क्लास को मेमोरीस्ट्रीम के रूप में तेजी से चलाने के लिए नहीं मिल सका।
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr)
{
try
{
int base_size = base_arr.Length;
int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]);
Array.Resize(ref base_arr, base_size + add_arr.Length);
Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T);
}
catch (IndexOutOfRangeException ioor)
{
MessageBox.Show(ioor.Message);
return false;
}
return true;
}
where T : struct
) के प्रकार पैरामीटर को सीमित करने का प्रयास कर सकते हैं , लेकिन - सीएलआर के दोषों में विशेषज्ञ नहीं होने के कारण - मैं यह नहीं कह सकता कि क्या आपको कुछ निश्चित संरचना पर अपवाद मिल सकते हैं। (उदाहरण के लिए यदि उनके पास संदर्भ प्रकार के फ़ील्ड हैं)
public static byte[] Concat(params byte[][] arrays) {
using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) {
foreach (var array in arrays) {
mem.Write(array, 0, array.Length);
}
return mem.ToArray();
}
}
सरणियों को संयोजित करने के लिए जेनरिक का उपयोग कर सकते हैं। निम्नलिखित कोड को आसानी से तीन सरणियों तक विस्तारित किया जा सकता है। इस तरह आपको विभिन्न प्रकार के सरणियों के लिए कोड की नकल करने की आवश्यकता नहीं है। उपर्युक्त कुछ उत्तर मुझे अत्यधिक जटिल लगते हैं।
private static T[] CombineTwoArrays<T>(T[] a1, T[] a2)
{
T[] arrayCombined = new T[a1.Length + a2.Length];
Array.Copy(a1, 0, arrayCombined, 0, a1.Length);
Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length);
return arrayCombined;
}
यहां @Jon Skeet द्वारा दिए गए उत्तर का सामान्यीकरण है। यह मूल रूप से एक ही है, केवल यह किसी भी प्रकार के सरणी के लिए उपयोग करने योग्य है, न केवल बाइट्स के लिए:
public static T[] Combine<T>(T[] first, T[] second)
{
T[] ret = new T[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static T[] Combine<T>(T[] first, T[] second, T[] third)
{
T[] ret = new T[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static T[] Combine<T>(params T[][] arrays)
{
T[] ret = new T[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (T[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
sizeof(...)
और गुणा करते हैं कि आप कितने तत्वों को कॉपी करना चाहते हैं, लेकिन जेनेरिक प्रकार के साथ आकार का उपयोग नहीं किया जा सकता है। यह संभव है - कुछ प्रकारों के लिए - उपयोग करने के लिए Marshal.SizeOf(typeof(T))
, लेकिन आपको कुछ प्रकारों (जैसे तार) के साथ रनटाइम त्रुटियाँ मिलेंगी। सीएलआर प्रकार के आंतरिक कामकाज के अधिक गहन ज्ञान वाला कोई व्यक्ति यहां सभी संभावित जाल को इंगित करने में सक्षम होगा। यह कहने के लिए पर्याप्त है कि जेनेरिक एरे का कॉन्सेप्टेशन विधि [ब्लॉककॉपी का उपयोग करना] लिखना तुच्छ नहीं है।
/// <summary>
/// Combine two Arrays with offset and count
/// </summary>
/// <param name="src1"></param>
/// <param name="offset1"></param>
/// <param name="count1"></param>
/// <param name="src2"></param>
/// <param name="offset2"></param>
/// <param name="count2"></param>
/// <returns></returns>
public static T[] Combine<T>(this T[] src1, int offset1, int count1, T[] src2, int offset2, int count2)
=> Enumerable.Range(0, count1 + count2).Select(a => (a < count1) ? src1[offset1 + a] : src2[offset2 + a - count1]).ToArray();
आप सभी को बाइट एरे की सूची को पास करने की आवश्यकता है और यह फ़ंक्शन आपको बाइट्स (मर्ज) के एरे को वापस कर देगा। यह सबसे अच्छा उपाय है जो मुझे लगता है :)।
public static byte[] CombineMultipleByteArrays(List<byte[]> lstByteArray)
{
using (var ms = new MemoryStream())
{
using (var doc = new iTextSharp.text.Document())
{
using (var copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (var p in lstByteArray)
{
using (var reader = new PdfReader(p))
{
copy.AddDocument(reader);
}
}
doc.Close();
}
}
return ms.ToArray();
}
}
कॉनसैट सही जवाब है, लेकिन किसी कारण से एक नियंत्रित चीज़ को सबसे अधिक वोट मिल रहे हैं। यदि आपको वह उत्तर पसंद है, तो शायद आप इस सामान्य समाधान को और अधिक पसंद करेंगे:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
जो आपको चीजों को करने देगा जैसे:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();